Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Pagemanager Laravel Package

backpack/pagemanager

Backpack PageManager gives your Laravel app a ready-made admin interface for managing static pages. Create pages from templates, edit content and SEO fields, control slugs and routes, and let clients update site content without touching code. Built on Backpack for Laravel.

View on GitHub
Deep Wiki
Context7

Getting Started

Install and wire up (5 minutes):

composer require backpack/pagemanager
php artisan vendor:publish --provider="Backpack\PageManager\PageManagerServiceProvider"
php artisan migrate

Then add the public catch-all route at the bottom of routes/web.php:

Route::get('{slug?}', '\Backpack\PageManager\app\Http\Controllers\PageController@index')
    ->where('slug', '.*');

The admin CRUD is registered for you at /admin/page (sidebar item, list, create/edit with the standard Backpack UI). The package gives you a pages table with template, name, title, slug, content, extras (JSON) and extend.

Look here first:

  1. config/backpack/pagemanager.php (older releases: config/laravel-page-manager.php) — this holds page_templates, the heart of the package. Each entry has a name, a view, and an array of Backpack fields.
  2. vendor/backpack/pagemanager/src/app/Http/Controllers/PageController.php — ~15 lines. Read it once; it defines the whole public contract (find by slug → abort(404) → render the template view with $page).
  3. vendor/backpack/pagemanager/src/app/Models/Page.php — a plain Eloquent model with CrudTrait.

First real use case: create an "About" page. Add a standard template to the config pointing at pages.standard, create resources/views/pages/standard.blade.php that @extends('layouts.app') and prints {!! $page->content !!}, then create the page in /admin/page. That's the entire loop — config → blade → admin.

Implementation Patterns

Template with custom fields. Anything you add under fields becomes an admin form input and is persisted into the extras JSON column, so you get per-template metadata without touching the schema:

'page_templates' => [
    'standard' => [
        'name'  => 'Standard',
        'view'  => 'pages.standard',
        'fields' => [
            ['name' => 'meta_title',       'label' => 'Meta Title',       'type' => 'text'],
            ['name' => 'meta_description', 'label' => 'Meta Description', 'type' => 'textarea'],
            ['name' => 'hero_image',       'label' => 'Hero',             'type' => 'image', 'crop' => true],
        ],
    ],
    'contact' => [
        'name'  => 'Contact',
        'view'  => 'pages.contact',
        'fields' => [['name' => 'map_embed', 'label' => 'Map', 'type' => 'textarea']],
    ],
],

In the blade view: $page->extras['meta_title'] ?? $page->title. If you prefer attribute access ($page->meta_title), add an accessor on a subclassed model rather than sprinkling array lookups through views.

Meta tags / layout integration. Don't hardcode SEO fields per template view. Resolve them once in the layout:

<title>{{ $page->extras['meta_title'] ?? $page->title ?? config('app.name') }}</title>

and render the sections from pages.* into named yields.

Custom data in the page. The public route is yours, so the cleanest override is a controller that resolves the view from config and injects extra data — no vendor file editing:

class PageController extends Controller
{
    public function index($slug = null)
    {
        $page = Page::where('slug', $slug)->firstOrFail();
        $template = config('backpack.pagemanager.page_templates')[$page->template] ?? abort(404);

        return view($template['view'], [
            'page'    => $page,
            'related' => Post::latest()->take(3)->get(),
        ]);
    }
}

Point the catch-all route at this controller instead. For truly global data (nav, footer), a View::composer('pages.*', ...) is even less invasive.

Seed pages for reproducible deploys. Pages are content, but the structure of key pages (home, about, legal) usually belongs in a seeder:

Page::updateOrCreate(['slug' => 'about'], [
    'template' => 'standard',
    'name'     => 'About',
    'title'    => 'About Us',
    'content'  => '<p>…</p>',
    'extras'   => ['meta_title' => 'About Us'],
]);

Reuse the model elsewhere. Page is a normal Eloquent model — use it for a sitemap (Page::pluck('slug')), a footer menu (Page::whereIn('slug', [...])), or Route::has-style link generation. Caching the query is worthwhile because the catch-all hits the DB on every request:

$page = Cache::rememberForever("page:{$slug}", fn () => Page::where('slug', $slug)->firstOrFail());

Bust it in a saved/deleted model observer keyed on the old and new slug.

Gotchas and Tips

  • Route order is everything. The {slug} / .* route must be the last route in web.php. Put it above anything else and it silently swallows /login, /contact-form POST targets' siblings, and every other GET route. If routes suddenly 404 or render the wrong thing, this is the first thing to check.
  • The catch-all is GET-only, so POST/PUT/DELETE routes are unaffected — but a /{username} style route defined after it will never fire.
  • Slugs may contain slashes (services/web-design) thanks to the .* constraint. That also means a leading slash or trailing slash in the slug breaks matching; normalize on save.
  • Template changes leave stale extras. Switching a page from standard to contact in the admin swaps the form fields, but the old JSON keys remain in extras. Harmless at render time, confusing during debugging — null-coalesce every extras lookup.
  • The template view file is not shipped. You create resources/views/pages/*.blade.php yourself; a missing file gives View [pages.x] not found, not a helpful package error.
  • config:cache after editing templates. Template definitions are config; on cached production configs, adding a field in the config file does nothing until you re-cache. Same for the view path.
  • No drafts, revisions, publishing workflow, or scheduling. If you need them, add a status/published_at column via migration plus a global scope on a Page subclass — don't expect the package to grow them.
  • No multilingual support. Either create one page per locale (about, en/about) or add a locale column and scope the controller query by app()->getLocale().
  • Overriding the admin CRUD is awkward. The package registers Route::crud('page', PageCrudController) itself. To customise it, copy the controller into app/Http/Controllers/Admin/PageCrudController.php and register it under a different URI (e.g. pages) in routes/backpack/custom.php, or extend the model instead of the form.
  • Slug auto-generation derives from name, not title. If you want slugs from the title, adjust the slug field in your own CRUD controller; otherwise train editors to fill name meaningfully (it's what appears in the admin list).
  • extras must be cast to array on any model subclass, or Eloquent hands you a raw JSON string and every view blows up with "Trying to access array offset on string".
  • 404s are silent abort(404) — no logging, no hint it was PageManager. When a page "doesn't exist" but the row is in the DB, check for trailing whitespace in the slug and whether the route is actually reached (php artisan route:list).
  • Version drift is real. The package tracks Backpack major versions and the config filename/namespace has moved between them; pin the major (^4.0, ^6.0, …) to match your installed Backpack and re-read the config after every upgrade.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
abbasudo/artisan-route
mirvan/ippanel
ignibyte/aic-framework
glavweb/data-schema-bundle
glavweb/datagrid-bundle
tacticmedia/scim-server
uni-method/frame
neika-ai/sdk
adachsoft/php-code-style
pharaonic/laravel-notification-dependencies
beautinow/translug
dev-brainiac/bot-cloaked
octoberrain/html
octoberrain/extension
octoberrain/exception
octoberrain/contracts
adachsoft/api-version-bundle
cgv/localized-resource
noith/settings
kda/laravel-package