rmzamora/page-bundle
A Laravel package for managing CMS-style pages in your application, bundling page models, routing, and rendering helpers so you can add editable content pages without building the plumbing yourself. Early-stage project with limited adoption; review the source and docs before relying on it in product...
Reality check first: rmzamora/page-bundle is a fork of sonata-project/page-bundle, a Symfony bundle. It is not a Laravel package and cannot be composer required into a Laravel app — its dependency chain (symfony/framework-bundle, sonata-project/admin-bundle, sonata-project/block-bundle, doctrine/orm, knp-menu) will not resolve against Laravel's container or routing. The fork is also archived, zero-star, zero-dependent, so treat it as a reference implementation, not a dependency.
Where to look first:
SonataPageBundle.php, Resources/config/*.xml, and Entity/Page.php for the domain model.PageManager, SnapshotManager, and BlockManager services — these are the day-to-day API.First use case (if you were on Symfony 3/4): install the bundle alongside SonataAdmin, then build a "Pages" admin section where editors compose pages from reusable blocks and publish snapshots. In Laravel your equivalent first use case is a Page model plus an admin resource where content is composed from ordered blocks — see below.
What the bundle actually does, in port-able terms:
Page row holds slug, title, site, template, and SEO metadata; a page_block join table attaches ordered Block rows. That is classic polymorphic block composition — in Laravel: Page hasMany PageBlock (polymorphic blockable).PageSnapshot row with a JSON payload of page + blocks, and the public route renders the snapshot instead of live-edited state. In Laravel: a page_snapshots table with a payload JSON cast, render the latest published snapshot on the front end.Site entity plus a site selector; pages are scoped by site_id. In Laravel that maps to spatie/laravel-multitenancy or a plain site_id global scope.BlockRenderer registry ('hero' => HeroBlock::class) keyed off a type column, rendered via @include('blocks.'.$type) behind a small Block contract.Practical Laravel workflow for the same thing:
php artisan make:model Page -m, then PageBlock, PageSnapshot, Site.Page using the Builder field — this is the direct analogue of block composition and gets you ~80% of SonataPage's editing UX in an afternoon.Route::get('/{page:slug}', PageController::class) resolving the latest published snapshot, wrapped in Cache::remember keyed by snapshot id.meta_title, meta_description, og_image on the snapshot; feed spatie/laravel-sitemap from published pages.Integration tips: keep block config in a PHP config array rather than DB schema, so adding a block type doesn't need a migration. Render blocks through Blade components (<x-block.hero :data="$block->data" />) to get the same template-swappable behaviour the bundle gets from Symfony service tags.
Entity/* wholesale.bin/console cache:clear and re-publishing were the standard fix. In Laravel, tag page cache by snapshot id and bust on publish.sonata:page:create-snapshots command and the sonata_page config tree are where most "why is it blank" issues live. Start there when reading source.How can I help you explore Laravel packages today?