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

Tomato Cms Module Laravel Package

tomatophp/tomato-cms-module

A Laravel CMS module from the TomatoPHP ecosystem that adds content management to your app: pages, posts, and menus with a Filament admin panel, multilingual support, and an extendable, drop-in structure for TomatoPHP projects.

View on GitHub
Deep Wiki
Context7

Getting Started

tomatophp/tomato-cms-module is the CMS layer of the TomatoPHP admin stack — pages (with localized content and SEO meta) exposed through Filament resources, meant to sit on top of tomato-admin / tomato-settings / tomato-roles.

composer require tomatophp/tomato-cms-module
php artisan tomato-cms:install   # publishes config/migrations, migrates, seeds permissions
php artisan migrate
php artisan optimize:clear

If the install command isn't present in your version, fall back to the documented publish tags:

php artisan vendor:publish --tag=tomato-cms-config
php artisan vendor:publish --tag=tomato-cms-migrations

Where to look first

  1. config/tomato-cms.php — feature toggles, route prefix/middleware, model bindings. This is the file you will touch most.
  2. The package ServiceProvider — see how routes, views (loadViewsFrom), translations, and Filament resources are registered.
  3. src/Models/Page.php — the central model; check casts, translated attributes, and relationships.
  4. src/Filament/Resources/* — the admin CRUD you'll be extending or hiding.
  5. routes/ — the frontend page route and its slug binding.

First use case: log into your Filament panel, create a Page with a slug, title, body, and SEO meta, then hit the frontend route to confirm it renders. That single loop exercises migrations, permissions, routing, view resolution, and the locale pipeline — if all five work, the rest of the module will behave.

Implementation Patterns

Admin-first, frontend-second. The package's center of gravity is the Filament resource. Most day-to-day customization means overriding the resource form/table (extra fields, section builders, publish toggles) rather than writing CRUD by hand. Extend the resource class and re-register it in your panel provider instead of forking the vendor file.

Slug routing on the frontend. Bind pages by slug in the route and let the controller return a view:

Route::get('/{page:slug}', [PageController::class, 'show'])->name('pages.show');

Keep the route last in your web group so it doesn't swallow other paths. If slugs are localized, resolve with the current locale and fall back to the default language.

Localized content. Translations are a relationship, so treat them like any other has-many: eager load (with('translations')), filter by locale once at the query level, and cache the resolved array — not the Eloquent model — so you don't leak a stale relation into the cache.

Cache + invalidate on save. Rendered CMS pages are the classic cache candidate. Wire it through model events so editors never see stale output:

static::saved(fn (Page $p) => Cache::forget("page:{$p->slug}:".app()->getLocale()));
static::deleted(fn (Page $p) => Cache::forget("page:{$p->slug}:".app()->getLocale()));

Permissions. The installer seeds permissions tied to tomato-roles. In multi-role setups, re-run the seeder after every module upgrade and verify the policy still maps to the right permission names — silent 403s in the panel are almost always a seeded-permission mismatch, not a code bug.

Settings integration. Site-wide CMS values (default meta image, analytics snippet, footer text) belong in tomato-settings, not in a page record. Read them through the settings helper in your layout so editors can change them without a deploy.

Testing. Use RefreshDatabase and assert on the resolved view data rather than rendered HTML — the package's Blade is vendor-owned and will change under you. Factories for Page plus a locale toggle covers 90% of regressions.

Gotchas and Tips

v1.0.3 changed the namespace — treat it as a breaking patch. The sole change in this release is a namespace update (PR #1, first-time contributor). Even though it's a patch bump, any of the following will break on upgrade:

  • Your own classes with use statements pointing at the old namespace.
  • Subclasses of the package's models, resources, or controllers.
  • Published config files or migrations that embed fully-qualified class names (e.g. 'model' => Old\Namespace\Page::class).
  • Cached config/routes referencing old class strings.

Upgrade checklist:

composer update tomatophp/tomato-cms-module
composer dump-autoload
php artisan optimize:clear
php artisan filament:optimize-clear   # if on Filament v3

Then grep your app/, config/, and database/ for the old namespace. If you published config or migrations, diff them against the package's stubs/ — vendor:publish --force overwrites, so back up your customizations first.

Config precedence bites. Published config in config/tomato-cms.php wins over the package default forever, including after upgrades. When a new release adds a key, your published file won't have it. Either keep published config minimal or re-publish on every minor bump and re-apply your diffs.

Migration ordering. The module's migrations assume tomato-admin/base tables and any translation tables already exist. If you install it into an existing app mid-history, run it after the base packages or you'll get foreign-key failures that look like package bugs.

Filament version drift. Check composer.json constraints before upgrading Filament — TomatoPHP modules historically lag a major version behind. A Filament major bump will silently disable resource discovery, and your panel will just be missing the Pages menu with no error.

Route caching. Module routes registered in the service provider are fine under route:cache, but closures are not. If you add a custom page route, use a controller action.

HTML sanitization. CMS body fields are typically rendered unescaped ({!! !!}). That's the point of a CMS, but it means any user with page-edit permission effectively has script injection. Gate the editor role tightly, and sanitize on output if untrusted authors exist.

Extension points, in order of preference: config-driven model swap → extend the Filament resource → override the Blade view via view()->prependNamespace('tomato-cms', ...) → fork. Forking is the last resort; this package is moving fast (three patches already), and a fork will cost you every future namespace and API fix.

Debugging toolkit. php artisan about for provider discovery, php artisan route:list --name=cms for frontend routing, and a dd() inside the resource's form() to confirm your override is actually the one being loaded — duplicate registrations are a common cause of "my changes do nothing" in TomatoPHP stacks.

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.
skyraptor/chatter
edlugz/sasapay
abbasudo/artisan-route
mirvan/ippanel
ignibyte/aic-framework
glavweb/data-schema-bundle
glavweb/datagrid-bundle
tacticmedia/scim-server
uni-method/frame
neika-ai/sdk
adachsoft/php-code-style
pharaonic/laravel-notification-dependencies
beautinow/translug
dev-brainiac/bot-cloaked
octoberrain/html
octoberrain/extension
octoberrain/exception
octoberrain/contracts
adachsoft/api-version-bundle
cgv/localized-resource