rz/page-bundle
rz/page-bundle offers a lightweight way to define, organize, and render reusable pages in Laravel/PHP applications. It provides bundle-style integration for page management, helping developers keep page content and templates structured with minimal setup.
Reality check before you install anything. This is rz/page-bundle, a Symfony 2-era bundle (RzPageBundle), archived, with zero stars and zero dependents. You are a Laravel developer, so the first step is not composer require — it is deciding whether this belongs in your composer.json at all. It almost certainly does not. If you are evaluating it because a ticket says "add editable pages," stop here and move to the Laravel-native pattern in Implementation Patterns below.
If you are forced onto it (e.g. auditing a legacy Symfony app that already vendors it), minimal steps are:
composer.json first. Check the symfony/*, doctrine/*, and php constraints against your runtime. A Symfony 4+/PHP 8 project will not resolve cleanly. This is a 10-minute composer why-not check, not a design exercise.PageServiceProvider here):// app/AppKernel.php
new Rz\PageBundle\RzPageBundle(),
# app/config/routing.yml
rz_page:
resource: "@RzPageBundle/Resources/config/routing.xml"
prefix: /
# app/config/config.yml
rz_page:
page_class: AppBundle\Entity\Page
Where to look first, in this order:
Resources/config/ — routing, DI service definitions, default config. This is where the actual behaviour lives; there is no README worth trusting.Entity/ — the Page model and its Doctrine mapping. This tells you the real schema: title, slug, body, SEO fields, parent/tree relationships, publication flags.Controller/ — how a slug is resolved and handed to a Twig template.DependencyInjection/ — the config tree, which is the only extension point you get.First use case: create one Page with a slug, a title, and a body, render it at /{slug} through the bundle's controller, and confirm the SEO metadata fields land in the template. That single loop is the whole package. If you cannot get it running in an afternoon, abandon it — it will not get easier, because nobody is upstream to fix it.
The pattern the bundle is trying to sell. Page-as-entity, resolved by slug, rendered through a per-page template, with SEO metadata attached to the record. That is a genuinely useful shape. Here is how it maps to what you should actually build.
If you are (unfortunately) using the bundle in Symfony: you extend the Page entity, set rz_page.page_class to your subclass, and add fields in your app's Doctrine mapping. Templates are selected per page via a template field or a resolver service. Slug resolution goes through the bundle's controller, which effectively does a findOneBySlug and 404s on miss.
The Laravel-native equivalent (do this instead). The bundle's domain is ~150 lines of Laravel you fully own:
// database/migrations/xxxx_create_pages_table.php
Schema::create('pages', function (Blueprint $table) {
$table->id();
$table->string('slug')->unique();
$table->string('title');
$table->longText('body')->nullable();
$table->string('meta_title')->nullable();
$table->string('meta_description')->nullable();
$table->string('canonical_url')->nullable();
$table->string('template')->default('pages.show');
$table->boolean('published')->default(false);
$table->timestamps();
});
// app/Models/Page.php
class Page extends Model
{
protected $guarded = [];
protected $casts = ['published' => 'boolean'];
public function getRouteKeyName(): string { return 'slug'; }
public function scopePublished($q) { return $q->where('published', true); }
}
// routes/web.php
Route::get('/{page:slug}', [PageController::class, 'show'])
->where('page', '.*') // allow nested slugs
->name('pages.show');
// app/Http/Controllers/PageController.php
public function show(Page $page)
{
abort_unless($page->published, 404);
return view($page->template, compact('page'));
}
This gives you the bundle's entire feature set — slug routing, SEO metadata, per-page templates — with route-model binding, Blade, and zero archival risk. An admin form (Filament, Nova, or a plain resource controller) is the remaining hour of work.
Workflows worth copying from the bundle conceptually:
pages.show, pages.landing, pages.legal) and let the controller pick it. This is the bundle's "flexible templating" idea, done cleanly./about/team), model parent_id and build slugs from the path. The bundle does this; Eloquent belongsTo/hasMany does it better.route('pages.show', $page) helper and cache slugs with a route-cache-friendly table.Integration tips for a Laravel shop:
composer require this into a Laravel app "to see." A Symfony bundle cannot be bootstrapped by Laravel's container without a bridge, and there is no maintained bridge.page_class config and the entity mapping as the contract, and port the data into the Laravel version above when you migrate frameworks.It is not a Laravel package, and no amount of config will make it one. The Bundle suffix and Rz\PageBundle\RzPageBundle registration are Symfony kernel conventions. There is no service provider, no facade, no config publish. Trying to graft it means fighting two DI systems for a marginal page table. This is an architectural mismatch, not a setup problem — the correct fix is not to install it.
Archived means no patches, ever. composer audit findings against it or its dependency tree have no upstream remediation. Every PHP minor bump is your ticket. If it is already in a tree you own, that is a remediation item, not a "keep an eye on it."
Pinned constraints will fight you. Symfony 2 bundles typically pin symfony/symfony or component constraints to 2.x/3.x ranges and old Doctrine. On PHP 8.2+ and Symfony 5/6/7 you will hit resolution deadlocks. Run composer require --dry-run in a throwaway project first; expect it to fail, and do not spend time relaxing constraints by hand — you are then maintaining a fork of an abandoned package.
page_class is the only real extension point — use it or don't adopt. If you must customise the Page model, set rz_page.page_class to your subclass. If you find yourself needing more than that, you have outgrown an archived bundle and should port.
No tests = unknown behaviour at the edges. There is no test suite to read as spec, so edge cases (nested slugs, unpublished pages, slug collisions, canonical URL fallbacks) are undefined until you debug them yourself. Profile page-render queries early: if the bundle hydrates relationships per request without eager loading, you get N+1s on any page tree.
Debugging tips for the legacy case: enable Symfony's profiler, check router:debug to confirm the bundle's routes are actually loaded (prefix conflicts with app routes are common), and inspect the compiled container (app/cache/dev) if the rz_page config node is silently ignored — mis-indented YAML under a bundle key is the usual culprit.
Do not fork it "just in case." Forking makes you the permanent sole maintainer of a package nobody else uses, with no community to absorb bugs. If the domain logic were irreplaceable a fork might be justified; here it is a page table, so build the Laravel version and close the ticket.
How can I help you explore Laravel packages today?