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

Pages Module Laravel Package

anomaly/pages-module

Pages module for the Anomaly CMS (Laravel/Streams) that adds structured, editable static pages to your site, with routing, templates, and admin management built in for quickly publishing and maintaining content-driven pages.

View on GitHub
Deep Wiki
Context7

Getting Started

anomaly/pages-module is not a standalone Laravel package — it is an addon for the Anomaly Streams Platform (the PyroCMS successor). The day-to-day value only appears once it sits inside a Streams application, so the first step is confirming your host app already requires anomaly/streams-platform and boots a distribution that registers addons.

composer require anomaly/pages-module
php artisan addon:install anomaly.module.pages   # runs the stream/field migrations

Then open /admin/pages, create a page with slug about, and hit /about. That round trip (CP entry → wildcard route → presenter → Blade view) is the mental model everything else hangs off.

Read in this order:

  1. composer.json — the anomaly/streams-platform constraint tells you which API surface you're coding against. Streams 1.x has drifted across minors; pin it.
  2. src/PagesModuleServiceProvider.php and src/PagesModule.php — migrations, route registration, view namespaces, asset publishing.
  3. src/Page/ — PageModel, PageRepository, PagePresenter, PageRouter, and the PageType directory. PageRouter is where a request path is matched against the pages stream and turned into a response; read it before you try to change routing behaviour.
  4. src/Page/Migrations/*_create_pages_fields.php — the canonical list of fields (title, slug, path, summary, content, meta_*, theme_layout, parent, enabled, home, roles). Everything you render comes from these.
  5. resources/views/ — the default page view you will almost certainly override.

First real use case: point the home flag at a page so / resolves through the module, then build one custom page type (e.g. a contact page) instead of hard-coding a route in routes/web.php.

Implementation Patterns

Reading pages from application code. Use the repository rather than raw Eloquent so Streams' locale/stream scoping is respected:

$page = app(\Anomaly\PagesModule\Page\PageRepository::class)
    ->findBy('slug', 'about');

// or in a Blade/controller context, if your version exposes it:
$page = \Anomaly\PagesModule\Page\PageModel::where('path', 'about')->firstOrFail();

In views, page entries are wrapped in a presenter, so $page->title, $page->path, $page->content, $page->meta_title, $page->layout are available without touching raw attributes. Check PagePresenter for the extra accessors (route(), href(), isHome()) before inventing your own.

Custom page types instead of new routes. This is the idiomatic extension point. Extend the module's PageType class, define a slug/name, and optionally override the route resolution or view it returns:

class ContactPageType extends \Anomaly\PagesModule\Page\PageType\PageType
{
    protected $slug = 'contact';
    protected $name = 'Contact Page';

    public function route(PageModel $page) { /* return Response/Redirect */ }
}

Register it with the module's page-type collection (in PagesModule::boot() or a small provider in your own addon). The page type persists on the entry, so editors pick it in the CP and you get a per-type rendering path without forking the module.

Override output, don't patch the module. Two layers, in preference order:

  • Per-page: resources/views/pages/{slug}.blade.php, falling back to the module's page view.
  • Per-layout: the theme_layout field selects a theme layout; set it in the CP rather than hard-coding a Blade @extends.

If you must change the shared markup, publish the module views (php artisan addon:publish anomaly.module.pages) and edit the published copies so upgrades don't overwrite you.

Extend the stream in your own addon. Never add fields by editing the module's migrations. Create a migration in your addon that calls Streams::make('pages')->fields(...) (or add fields through the Streams admin UI, which is the fastest path for one-off fields). You keep the custom field through a composer update of the module.

Listen to lifecycle events. The module dispatches entry events per page (PageWasCreated, PageWasSaved/PageWasUpdated, PageWasDeleted) under Anomaly\PagesModule\Page\Event\*. Register listeners for cache warming, sitemap invalidation, or pushing a slug change into a redirect table. Note that automatic 301s on slug change come from anomaly/redirect-module listening to these — if you don't have it installed, nothing happens.

Access-gated pages. The roles relation / allowed-roles fields let you scope a page to logged-in groups. Use this instead of writing middleware for member-only marketing pages; use middleware when the rule is dynamic (subscription tier, etc.).

Gotchas and Tips

  • The route is a catch-all. The module registers a wildcard path route and deliberately resolves late so /admin, /api, and your own web.php routes win. If a page 404s, run php artisan route:list and confirm the wildcard is last; if your own routes are being swallowed, they're registered after addon providers — check ordering and route:cache state.
  • Caching bites in three places. Streams caches the compiled stream/field registry, Laravel caches routes, and the CP caches asset manifests. When a new field or view override "doesn't appear," clear all three (cache:clear, route:clear, view:clear) before debugging code.
  • addon:uninstall drops the stream and its data. It is not a rollback. Snapshot the pages, pages_pages_translations, and related field tables before you reinstall to test a migration.
  • Locales. With localization on, page content lives in a translations table and is scoped to the active locale. A query returning an object with empty title almost always means locale mismatch, not a broken field.
  • enabled and home are just fields. The router filters on enabled (so drafts 404 rather than 403), and home is a single-page flag the CP maintains — direct DB edits can leave two pages marked home, and which one wins is undefined. Set it through the CP or normalise it in your own command.
  • PHP/platform constraints are the real install risk. Streams Platform has historically pinned older PHP ranges; on modern PHP you may need --ignore-platform-reqs or a maintained fork. Verify against the require block rather than the README, and sanity-check that addon:install actually created the pages tables.
  • Class names move between minors. With 8 stars and no dependents, this package has effectively no community documentation and no external test harness. Treat src/ as the spec: when a method you remember from PyroCMS docs is missing, grep the module (and streams-platform) for the current name instead of trusting blog posts.
  • Best debugging lever: php artisan tinker and app(PageRepository::class)->findBy('path', 'about') to see exactly what the router sees, plus dd() inside PageRouter when the match succeeds but the response is wrong. Almost every "pages module is broken" report is either a routing-order problem or a cache problem.
  • Preferred extension points, ranked: custom PageType → view override/publish → event listener → rebinding the repository/presenter in your provider. Forking the module is last because you inherit the maintenance of an already-thin package.
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.
unabomber/timezone
delatbabel/contacts
astritzeqiri/g-recaptcha
kevinongko/laravel-genode
developeruz/laravel-analytics
robbiep/afterthedeadline
frankkessler/incontact-laravel-oauth2-rest
jlxh/laravel-userlog
davidmgilo/timetables
qylinfly/role-permission
reflect-digital/conduit
takashato/laravel-vietqr
aitor24/laralang
kraenzle-ritter/entity-relations
dindigital/din-image
pomirleanu/image-colors
sawmainek/laravel-api-generator
delatbabel/jsonresponses
mouhamedfd/permissions-generator
eduplus/qr