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

Laravel Pages Laravel Package

rinvex/laravel-pages

Rinvex Laravel Pages gives Laravel apps a ready-made CMS-style page system: a flexible Page model with multilingual translations, slugs, SEO metadata, polymorphic relations, and route/caching support, so you can add editable static pages without building custom infrastructure.

Deep Wiki
Context7

Getting Started

Status check first: this package is archived and unmaintained. Confirm the laravel/framework constraint in composer.json before you invest time — the last releases target Laravel 5.5–8.x. On Laravel 9+ you'll usually need to fork, bump constraints to illuminate/*, and test the transitive rinvex/laravel-support dependency yourself.

Minimal install:

composer require rinvex/laravel-pages
php artisan vendor:publish --tag="rinvex-pages:config"
php artisan migrate

Look at these files first, in order:

  1. config/rinvex.pages.php — model bindings and table names (usually rinvex.pages.models.page and rinvex.pages.tables.pages).
  2. database/migrations/*_create_pages_table.php — the real contract. Expect uri, title, content, active, timestamps; title/content are often JSON columns for translations.
  3. src/Models/Page.php — $fillable, casts, and any traits (HasTranslations, slug/URI handling).
  4. src/Providers/PagesServiceProvider.php — whether it registers a catch-all route and under which middleware/prefix.

First use case: a small set of static marketing/legal pages ("about", "terms", "privacy") that non-developers edit, resolved by URI at request time.

use Rinvex\Pages\Models\Page;

Page::create([
    'uri'     => 'about-us',
    'title'   => 'About Us',
    'content' => '<p>Who we are.</p>',
    'active'  => true,
]);

Render it in a controller or fallback route:

$page = Page::where('uri', ltrim($request->path(), '/'))
    ->where('active', true)
    ->firstOrFail();

return view('pages.show', compact('page'));

Implementation Patterns

Let the package own the route, or own it yourself — not both. If the service provider registers a catch-all ({uri} with .*), your app routes must be registered before it; otherwise the catch-all swallows /login, /api, etc. If the package does not ship a route (or you disable it in config), register a fallback deliberately:

Route::fallback(function (Request $request) {
    $page = Page::where('uri', $request->path())->where('active', true)->firstOrFail();
    return view('pages.show', compact('page'));
});

A fallback route is preferable to a {uri} catch-all: it runs only when nothing else matches, so it can't shadow real routes.

Admin CRUD. Keep the package as the persistence layer and put authorization/policy in your own controller. Normalize the URI in a form request (Str::slug, strip leading/trailing slashes) before saving, then:

$page->update($validated);

Translations. If title/content are JSON-translated columns, mass-assign locale arrays and resolve per request:

Page::create([
    'uri'   => 'about-us',
    'title' => ['en' => 'About Us', 'ar' => 'من نحن'],
    'content' => ['en' => '<p>...</p>', 'ar' => '<p>...</p>'],
    'active' => true,
]);

$page->getTranslation('title', app()->getLocale());

Wrap this in a locale-aware resolver rather than scattering getTranslation calls through Blade.

Caching. Pages change rarely and are read on every request — cache aggressively and invalidate from an observer, not from controllers:

class PageObserver
{
    public function saved(Page $page) { Cache::forget("page:{$page->uri}"); }
    public function deleted(Page $page) { Cache::forget("page:{$page->uri}"); }
}

$page = Cache::remember("page:{$uri}", 3600, fn () => Page::where('uri', $uri)->where('active', true)->firstOrFail());

Menu/sitemap integration. Query active pages once per request and feed them to layout/nav builders:

$navPages = Page::where('active', true)->orderBy('title')->get(['uri', 'title']);

Blade rendering. Keep a single pages.show view that switches on a page "template"/"layout" column if you add one (see extension points below), rather than storing full Blade in content.

Gotchas and Tips

  • active is almost certainly not a global scope. Nothing stops a draft page from being served if you forget where('active', true). Centralize the lookup in one repository/query scope and never duplicate it.
  • URI normalization is the #1 cause of "page exists but 404s." /about, about, and about/ are different strings. Normalize on write (mutator or form request) and on read (same helper).
  • Route ordering / catch-all conflicts. If the package registers a wildcard route, php artisan route:list will reveal it. Move app routes above it or replace it with Route::fallback.
  • Mass-assignment surprises. active must be in $fillable and cast to bool; otherwise "0"/"false" from forms will round-trip as truthy strings.
  • Raw HTML is stored as-is. If authors aren't fully trusted, sanitize content (HTMLPurifier, mews/purifier) before render; {!! $page->content !!} is an XSS sink by design.
  • Translated JSON columns are painful to query/sort. Don't where('title', ...) or orderBy('title') on a translated column across databases; sort in PHP or add a non-translated sort_title.
  • Dependency drift. Because the package is archived, its rinvex/laravel-support constraint may pin old illuminate/* versions and block unrelated upgrades. Isolate the fork or vendor the two classes you actually use.
  • Migration collision. If you need columns (template, meta_title, published_at), don't edit the package migration in vendor/; publish it or add a follow-up migration.
  • Extension points: swap the model in config/rinvex.pages.php (models.page) and extend Rinvex\Pages\Models\Page; register observers for cache invalidation; override the view/route rather than the service provider; add a templates table or template column if you need more than one layout.
  • Testing: there's unlikely to be a factory. Use RefreshDatabase and Page::create([...]) in setUp, and write a feature test asserting draft pages 404 and active pages 200 — that single test catches the scope and URI-normalization bugs above.

Bottom line: as an archived, ~40-star package it's a thin Eloquent model plus a wildcard route — you're adopting the schema and conventions more than the code. For a legacy Laravel 8 app with a handful of CMS pages it's workable; for anything new, a 40-line Page model, migration, Route::fallback, and an observer gives you the same functionality with none of the dependency risk.

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.
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console