pvtl/voyager-pages
A Pages module for Laravel Voyager that adds admin CRUD for managing CMS pages, including titles, slugs, content, and templates, so you can build editable site sections without custom code.
This is an archived module from January 2020 that adds a CMS-style "Pages" entity to a Voyager admin. Treat it as legacy: verify compatibility before installing rather than after.
composer require pvtl/voyager-pages
If resolution fails, pin the last tagged release or a commit, and expect to fork.php artisan vendor:publish --provider="Pvtl\VoyagerPages\VoyagerPagesServiceProvider"
php artisan migrate
database/seeds in the package for the exact class name).Where to look first, in order: the service provider in src/ (it tells you what routes, config, and views are registered), the migration (the column list is the model's contract), the package's routes file (how slugs resolve), then config/voyager-pages.php if present.
First use case: static, editor-owned content — About, Terms, Privacy, landing pages — so marketing can edit copy without a deploy.
Admin-driven content. The normal workflow is: create/edit pages in the Voyager BREAD, publish via a status/active flag, and let the package's slug route resolve them. Keep non-developers in the admin and reserve code changes for templates and new fields.
Programmatic seeding for reproducible environments. Don't hand-create pages in production-only. Seed them with updateOrCreate keyed on slug so staging/prod match and re-runs stay idempotent:
Page::updateOrCreate(
['slug' => 'privacy-policy'],
['title' => 'Privacy Policy', 'body' => '...', 'status' => 1]
);
Linking. Reference pages by slug in Voyager's menu builder and in Blade via route()/url() against the slug rather than hardcoding paths, so a slug change is a one-line edit.
Template selection. If the package doesn't already support it, add a template column and resolve the view dynamically — this is the cheapest way to get "landing page" vs "legal page" layouts from one BREAD:
return view($page->template ?: 'pages.default', compact('page'));
Rendering overrides. Publish the package views and shadow them under resources/views/vendor/..., or point the slug route at your own controller if you need middleware (auth-gated pages) or shared view data (menus, SEO meta).
Caching. Page lookups are a natural cache target. Cache Page::where('slug', $slug)->first() and bust it from the model's saved/deleted events; tag the cache by slug so invalidation is surgical.
Headless/API use. If you have a SPA, expose the Page model through a small JSON controller rather than rendering Blade — the model is the reusable part; the view layer is the throwaway part.
composer.json, commit the lock file, and budget for a fork if you need to upgrade the host app.{slug} route will swallow any route registered after it. Ensure the package's route is registered last, or constrain it (->where('slug', '[A-Za-z0-9\-\/]+')) and keep admin, login, api out of the slug space.parent/child paths are supported before designing URLs that assume it; a plain {slug} segment won't match slashes without a custom pattern.ModelNotFoundException stack trace, and that unpublished pages are excluded from the query — not just hidden in the view.php artisan route:cache will fail in production. Convert to controller actions in your fork if you need it.php artisan storage:link or page images silently 404.updateOrCreate before putting it in a deploy pipeline.php artisan config:cache in deploys will mask later config edits — clear it during debugging before assuming the package ignores your settings.where('slug', ...) across locales needs care, or you'll get false negatives.php artisan route:list --path=<slug> to confirm the route exists and its order, php artisan tinker to test the model query directly, then check the published views are actually the ones being rendered.vendor/ — fork and use a VCS path repository instead.Page model plus a Voyager BREAD takes an afternoon and removes a dead dependency. Adopt this package only where it's already embedded in a legacy app you're maintaining, not modernizing.How can I help you explore Laravel packages today?