Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Page Bundle Laravel Package

integrated/page-bundle

integrated/page-bundle is a Laravel package for adding structured page management to your application. It offers a foundation for editable, route-aware pages that integrate with existing Laravel projects, with minimal setup and room to extend the content model to fit your needs.

View on GitHub
Deep Wiki
Context7

Getting Started

Confirm what you're installing before you write a line of code. integrated/page-bundle targets the Integrated CMS, which is built on Symfony, not Laravel. There is no ServiceProvider, no publishable config, no facade, no Illuminate contracts anywhere in the package. The bundle ships the Symfony idiom: a DependencyInjection extension with a Configuration tree, tagged service definitions, and bundle-registered routing plus Twig templates. If your application is Laravel, your first task is not installation — it's choosing a posture:

  1. Native Symfony host — you are genuinely inside an Integrated/Symfony app. Install normally.
  2. Headless sidecar — run the Integrated app as a content-authoring service and consume published pages from Laravel over HTTP. Recommended if the requirement is real.
  3. Reimplement in Laravel — take the model ideas, not the code. For a page CRUD plus a simple route resolver this is usually a one-to-two-sprint job and removes the vendor dependency entirely.

Do not try to shim the bundle into a Laravel container. The impedance mismatch across the container, config tree, router, and Twig inheritance will cost more than option 3.

If you are on Symfony/Integrated, the minimal path is:

// config/bundles.php
Integrated\Bundle\PageBundle\IntegratedPageBundle::class => ['all' => true],
# config/routes.yaml — verify the actual routing file name in the repo
integrated_page:
    resource: '@IntegratedPageBundle/Resources/config/routing.xml'
    prefix: /

Then run the bundle's migrations (bin/console doctrine:migrations:migrate after checking where they live), and open the DI extension's Configuration.php — that class is your real documentation for what is configurable (templates, page types, publishing states).

If you are on Laravel, minimal reimplementation to first page:

  • pages table: id, slug (unique), title, template, body, status enum(draft|scheduled|published), published_at, seo_title, seo_description, canonical, updated_at.
  • A resolver service PageResolver::findPublished(string $slug) that returns 404 for missing, 410 for retired, and honours published_at <= now().
  • One catch-all route Route::get('/{slug}', PageController::class)->where('slug', '.*') registered last, so real application routes win.
  • A Blade view per template name, resolved through an explicit whitelist map — never view($page->template) directly.

First use case to validate: create one page per template, publish one and leave one as draft, confirm the draft 404s for anonymous users and previews for an authenticated editor. Then, if you're native, edit it in the front end via website-bundle and confirm the published render actually changes. That loop — create, edit inline, publish, render — is the entire value proposition, and it's the only thing worth spiking first.

Implementation Patterns

Treat the page entity as the join point, not the whole feature. The page row should carry slug, template key, publish state, publish timestamp, and SEO fields. Everything else (blocks, media, relationships) should hang off it. Overloading the page table with structured content is what makes the eventual migration painful.

Route resolution: one catch-all, explicit precedence. The bundle's "conflict-safe resolution" is really slug-collision avoidance. Mirror that discipline in any integration:

// Register real routes first, then:
Route::get('/{slug}', [PageController::class, 'show'])
    ->where('slug', '^(?!admin|api|login|_debugbar).*$')
    ->middleware('page.cache');

Reserve prefixes for /admin, /api, /assets, and any locale segment. Decide now whether slugs are global or per-locale — retrofitting that later is a migration plus a redirect map.

Template strategy: whitelist, then map. Keep a config array of allowed template keys to view names. Editor-selected templates are a common path to arbitrary view inclusion.

// config/pages.php
'templates' => [
    'default'   => 'pages.default',
    'landing'   => 'pages.landing',
    'contact'   => 'pages.contact',
],

Throw on an unknown key at save time, not render time. A page with a dangling template should fail in the editor, not with a 500 in production.

Draft/preview pattern. Preview needs three things simultaneously: a signed, time-limited token in the URL; cache bypass (Cache::tags(["page:{$id}"])->flush() or simply no-store); and X-Robots-Tag: noindex plus a robots.txt disallow on the preview path. Never gate preview on "is logged in" alone — editors share links, and that link will eventually be pasted into a public channel.

Front-end editing is a separate render path. With website-bundle, in-context editing injects an editing overlay into the page. Keep the edit surface and the public render path separable: the overlay should be a distinct middleware/route guard, and the write endpoints it calls must be authenticated server-side and CSRF-protected. If the overlay is only hidden by CSS, it is not secured.

Headless sidecar integration (Laravel side). If you go this route, keep the coupling to one class:

final class PageClient
{
    public function __construct(private Http $http) {}

    public function bySlug(string $slug): ?PageData
    {
        return Cache::remember("page:{$slug}", 3600, fn () =>
            PageData::fromArray(
                $this->http->get("/api/pages/{$slug}")->throw()->json()
            )
        );
    }
}

Wire a queued job on a schedule to warm or purge cache by updated_at. Do not call the sidecar synchronously from the public request path — a slow editor session must never slow down a visitor.

Purge on write, tag by page and by collection. Every inline edit must invalidate the page itself and any listing, navigation, or sitemap fragment that embeds it. If the bundle offers no invalidation hooks, you own this layer; build it against a key convention from day one.

Extension points, in order of preference: override Twig templates by placing files in templates/bundles/IntegratedPageBundle/...; listen to the bundle's dispatched events (check Event/ in the repo); decorate services; extend the entity only as a last resort, because subclassing a Doctrine entity drags you into inheritance mapping and makes upgrades harder.

Gotchas and Tips

  • The framework mismatch is the headline gotcha. Assume Symfony semantics everywhere: bin/console, Twig, Doctrine, service tags. Budget 1–2 weeks of Symfony ramp-up for a Laravel-fluent team before anyone can confidently patch the bundle — and you will be patching it, because there are zero dependents upstream to catch regressions.
  • Config changes need a cache clear. Editing the DI extension's Configuration tree does nothing until bin/console cache:clear (and cache:warmup in prod). This is a classic half-hour wasted on "my config is ignored."
  • Slug uniqueness is not the same as route safety. Enforce uniqueness on slug at the database level and reserve framework prefixes in application code. A page at /api will shadow your API for everything under it.
  • Scheduled publishing and time zones. Store published_at in UTC, evaluate comparisons in UTC, and render dates in the site's display zone. Mixed storage is the usual source of "it published a day early/late" reports.
  • Canonical URLs break behind proxies and multi-host setups. Generate canonicals from a configured app.url, not from Request::getHost(). Otherwise staging, previews, and www/non-www redirects poison your SEO metadata.
  • Do not trust the editing route in production. The highest-severity failure mode in this package category is the editorial surface being reachable anonymously. Add a server-side authorization middleware on every write controller, verify it in a test, and check it again after every upgrade.
  • Debug with the standard Symfony tools, not with dd(). bin/console debug:router, debug:container --tag=..., and the profiler are how you find out whether the page route is actually registered and which service handles it. If the router shows nothing, the bundle is not registered — check bundles.php.
  • Migrations will collide with an existing content schema. Check for table prefixes before running anything against a shared database, and verify whether the bundle owns its tables or expects to sit alongside existing ones. Running its migrations blind on a database with a page table already present is unrecoverable without a backup.
  • Vendor and pin. With zero stars and zero dependents, no release is validated by anyone else. Commit a pinned version constraint, keep your fork in VCS, and record an ADR explaining why you chose it. At minimum two engineers should be able to read the bundle — the bus factor here is real.
  • Test the publish-state matrix, not just the happy path. Draft anonymous, published anonymous, scheduled-before-time, scheduled-after-time, archived, unknown slug, and a slug with a trailing slash. That table is nine cases and catches nearly every resolver bug.
  • If you're on Laravel, the cheapest correct answer is usually to build the page model yourself. The bundle's ideas — route-aware pages, template whitelisting, publish states, inline editing — are a day of design and a sprint of implementation. The dependency, not the code, is the expensive part.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
robertmarney/lara-hierarchial-collections
simonschaufi/rule-doc-generator
emissaoja/emissaoja
blackbricksoftware/gravityforms-civicrm-form-processor
al-rahabi/fancy-crud
jinas/dhivatar-component
mltstephane/laravel-quality
bernskiold/laravel-talentlms
bernskioldmedia/laravel-form-architect