th3mouk/cms-page-bundle
A Symfony bundle for CMS page management: add editable content pages to your application with reusable page entities and a simple workflow for creating, storing, and rendering static content. A lightweight option for projects that need basic CMS pages without a full CMS.
First, resolve the structural mismatch. This is a Symfony bundle, not a Laravel package. There is no service provider, no facade, no config/cms.php, no Blade views, no Eloquent model. If your application is Laravel, this evaluation closes here — you cannot composer require your way into a usable thing. The rest of this section assumes a Symfony application that already runs (or is willing to run) sonata-project/page-bundle, which is what this bundle wraps.
Compatibility is the real first step. The repo is archived, 1 star, 0 dependents, with no visible CI. Before installing anything, open composer.json in the repo and read the version constraints it pins for symfony/*, sonata-project/*, and PHP. Expect Symfony 2.8/3.x-era and PHP 5.x/7.0 pins. If your app is on PHP 8.2+ and Symfony 6/7 (which a modern Laravel team's Symfony sibling almost certainly is), you will be patching the bundle's constraints before you get a boot. Do a throwaway composer require in a scratch kernel and read the lockfile: that is the fastest truth you will get.
Install and bootstrap the whole Sonata stack, not just this bundle. Register bundles in AppKernel in dependency order (sonata core → block → admin → doctrine ORM admin → page → this bundle). Sonata is order-sensitive; a wrong order gives you a confusing "service not found" during boot.
Where to look, in this order:
DependencyInjection/Configuration.php and the extension class — this is the entire "simplification." It tells you which Sonata options are pre-set, overridden, or defaulted. This is the only place the bundle's value actually lives.Resources/config/*.yml — the services, admin classes, and block services it registers or replaces. Look for service IDs matching sonata.page.*, sonata.admin.*, sonata.block.* — those are overrides you inherit whether you want them or not.Resources/views/ — the default templates it ships; they are likely minimal and will need replacing.First use case, end to end: boot /admin → create a Site (host + is_default: true) → add a localhost site so dev doesn't 404 → create a Page with a template key → drop a container block, then a text block → take a snapshot → publish it → hit the configured host and confirm the render. That loop is the whole package; if any step fails, treat it as a hard blocker rather than a fixable annoyance.
The day-to-day workflow is: template → page → blocks → snapshot → publish. All editing happens in the Sonata admin at /admin/sonata/page/page. Developers rarely touch this bundle's PHP; they extend config and templates.
Define page templates in config, not code. A template is a key in sonata_page.templates with named block zones. Marketing pages need one template ("content page: header, body, footer"), legal pages another, landing pages another. Keep the template list small and stable — every template is a contract between editors and frontend, and changing zones after pages exist is painful.
Custom blocks are the extension point you'll actually use. Extend AbstractBlockService, implement BlockServiceInterface, tag the service sonata.block, register it in sonata_block.blocks config, then add an admin class so editors can configure it. If you skip the config registration, the block exists but never appears in the composer UI — a common half-hour of confusion.
Override entities for anything real. Every serious project ends up extending Page, Site, Block, or Snapshot — adding SEO fields, a noindex flag, a publish-at timestamp, translations. The pattern is: your own entity extends the Sonata one, your Doctrine mapping points at it, and you override the corresponding sonata.page.admin.* service or class parameter. Before committing to that pattern, check whether this bundle already forces its own entity classes — if it does, your override will collide with its DI extension and you'll be debugging parameter precedence instead of shipping pages.
Multi-site routing. Enable sonata_page.multisite, mark exactly one site is_default: true, and inject SiteSelectorInterface when you need the current site in a controller or listener. Without multisite, do not enable it — the selector becomes a source of nulls you'll chase.
Rendering is a listener, not a controller. Pages render through sonata.page.cms_manager via CmsManagerSelector, driven by the PageRenderer request listener. You almost never render pages by hand. If you find yourself calling the CMS manager directly in a controller, you're probably fighting the routing and should configure it instead.
If you genuinely want this ergonomics layer, reimplement it. The bundle's value is pre-set Sonata config plus a couple of admin conveniences. A small internal Symfony bundle — one extension class, one config tree, a handful of service overrides — reproduces that in 1–3 days with no abandoned-dependency liability and no fork to own. That is the pattern worth copying from this repo; the code is not.
The Laravel mismatch is the loudest gotcha. Frameworks aside, "Laravel/PHP" in any package index is not sufficient evidence of Laravel compatibility. Verify the framework before the feature matrix. This evaluation should be closed on that basis alone.
composer require will not work without an abandoned-package allowance and, on modern PHP, without patching constraints. Budget a day for a timeboxed spike before anyone claims it's "small." Success criterion is a rendered page, not a code read.
Zero dependents means zero community debugging. No Stack Overflow, no issue corpus, no forks to crib from. Every problem is a Sonata problem, so your onboarding doc is Sonata's documentation, not this repo's README. Plan ramp-up as "days to weeks on Sonata," not "one day on the wrapper."
Snapshot/publish is not optional and is the #1 "why isn't my page live" bug. Editing a page in the admin does nothing until you snapshot and publish. Put this in editor training on day one.
Bundle order and Sonata version skew are the top boot failures. If boot fails, diff the installed Sonata versions against the ones this bundle pinned before debugging anything else — the wrapper was written against a specific Sonata minor and will not tolerate drift.
Do not composer update without a constraint audit. Once present, the wrapper's EOL pins propagate to symfony/* and sonata-project/* and become an upgrade deadlock. The failure mode is silent: your app is fine until you try to move to a supported PHP/Symfony line, then nothing resolves.
If forced to adopt, fork — don't depend. MIT makes forking legally trivial. Pin to a commit SHA, vendor it internally, name an owner, register it in a dependency-risk register, and schedule a review per Symfony major. Also security-review the full transitive tree, because there is no upstream CVE channel here — an archived repo receives no patches, ever.
Extension points to know, in priority order: sonata_page.templates (what editors can build), sonata_block.blocks (what blocks appear), admin class overrides (what editors can configure), and entity overrides (what data exists). Everything day-to-day happens in that order.
How can I help you explore Laravel packages today?