revision-ten/cms
A flexible CMS for Laravel with pages, posts, media, menus, and SEO tools, plus a clean admin UI and Blade-based templating. Publish content quickly, extend via config, and tailor every part to fit your application.
composer require revision-ten/cms, but before writing any code open three things: composer.json (to confirm the Laravel/PHP constraints it supports), README.md, and the src/ tree. With a package at this maturity level, the source is the real documentation — the README is usually a skim.composer.json for extra.laravel.providers. If it's absent (typical for older packages), add the provider manually to config/app.php or bootstrap/providers.php depending on your Laravel version.php artisan vendor:publish --provider="RevisionTen\Cms\CmsServiceProvider"
php artisan migrate
Inspect database/migrations/ afterward — CMS packages frequently ship page, block, and revision tables, and you need to know what exists before you build on top of it.php artisan route:list to see what the package registered (admin panel, API, catch-all page route). This tells you whether it expects to own frontend routing or whether you wire it yourself.routes/web.php that resolves a slug to a page and renders it through a Blade view. That single loop — create content → resolve → render — exercises config, models, and views simultaneously.Slug-based catch-all routing. Most CMS packages of this shape expect a low-priority catch-all. Register it last in routes/web.php so your app routes win:
Route::get('/{slug?}', [PageController::class, 'show'])->where('slug', '.*');
Resolve via the package's page repository/model, return 404 when no match exists, and cache the lookup.
Block/component composition. Typical day-to-day work with a CMS package is registering block types and rendering them. Look for a block registry or config array where you map a block key to a view (and optionally a Livewire/Blade component). Keep the mapping in a dedicated config file so designers can add block types without touching PHP.
Wrap the package behind your own layer. Don't let controllers, views, and Blade templates call package facades directly. Introduce a thin PageRepository / ContentService that wraps model queries and returns your own DTOs. When (not if) you need to swap a query, add caching, or patch around a bug, you change one class instead of a dozen views.
Cache aggressively, invalidate on save. CMS packages are read-heavy. Cache resolved pages by slug + locale, keyed to a version, and flush that version inside a model saved/deleted observer. This is the single highest-value pattern for performance.
Headless/API usage. If the package exposes API routes, consume them from your SPA via the JSON endpoints rather than the Blade layer; if not, add a small controller that serializes pages to arrays. Keep this in a separate route group so you can rate-limit independently.
Admin panel as a separate guard. If an admin UI is shipped, move it onto its own route prefix, middleware, and guard. Don't let it share the default web guard — content editors and app users are distinct audiences.
Seeder-driven fixtures for tests. Create pages/blocks in factories or seeders so feature tests can hit your catch-all route and assert markup. This is the fastest way to verify that package upgrades don't break rendering.
"revision-ten/cms": "1.2.*" or a commit hash), and read CHANGELOG.md before any update. If a bug blocks you, fork it or copy the relevant classes into app/ rather than patching vendor/.vendor:publish without a --tag may clobber customized config or views. Publish once, commit the published files, and use tags from then on. If the package doesn't support tags, diff before re-publishing.php artisan route:list (are the package routes even registered?), php artisan config:clear after config edits, storage/logs/laravel.log, and php artisan tinker to query the page model directly. If routes 404, it's almost always provider registration or route precedence./{slug} will shadow any route registered after it, including admin and API endpoints. Always register content routes last and use where() constraints.published_at flag rather than guessing.resources/views/vendor/...), and model save/delete events. Prefer these over subclassing package models — subclassing tends to break relationship resolution.How can I help you explore Laravel packages today?