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 Laravel Package

tomatophp/tomato-cms

Tomato CMS is a Laravel package from the TomatoPHP ecosystem that adds content management to your app: pages, posts, menus, and media, with a Filament admin panel. It plugs into TomatoPHP's module system for quick setup. Early-stage and lightly adopted, so review before production use.

View on GitHub
Deep Wiki
Context7

Getting Started

Day-to-day with tomatophp/tomato-cms starts with the admin panel, not the codebase. The package is a Filament content module — pages, posts, categories, SEO metadata, multilingual fields — dropped into a Laravel app that already runs Filament (and ideally the wider Tomato PHP stack: admin, settings, roles, translations, media).

Where to look first, in order:

  1. composer.json and the resolved tree. Filament major alignment is the gate. composer show --tree tomatophp/tomato-cms | tee deps.txt tells you what you actually signed up for.
  2. Publish and read config before migrating. php artisan vendor:publish --provider="TomatoPHP\TomatoCms\TomatoCmsServiceProvider" — inspect config/tomato-cms.php and the migrations before php artisan migrate. Diff against any existing pages/posts/categories tables.
  3. The Eloquent models. TomatoPHP\TomatoCms\Models\* is the real contract — translated columns, slug source, publish flag, SEO fields. Read these before the Filament resources; the resources are generated ergonomics.
  4. The service provider. It's the documented extension point. Read what it registers — routes, migrations, Filament resources, translations, menus — so nothing is implicit.

First use case that pays off immediately: an internal back office or MVP where editors need page/post CRUD with slugs, publish toggles, categories, and SEO metadata inside a panel you'd otherwise hand-build. Prove one loop end-to-end: admin creates a page → publishes → front end renders it by slug. That loop is your whole value proposition; everything else is optimization.

# Throwaway branch, not main
composer require tomatophp/tomato-cms
php artisan vendor:publish --provider="TomatoPHP\TomatoCms\TomatoCmsServiceProvider"
# read config + migrations, then:
php artisan migrate

Implementation Patterns

Wrap it behind your own seam from day one. With a 1-star, single-maintainer package, this is the highest-leverage pattern you'll write:

interface ContentRepository
{
    public function pageBySlug(string $slug, string $locale): ?ContentPage;
    public function publishedPosts(int $limit = 10): Collection;
    public function menu(string $key): Collection;
}

final class TomatoContentRepository implements ContentRepository
{
    public function pageBySlug(string $slug, string $locale): ?ContentPage
    {
        return ContentPage::query()
            ->where('slug', $slug)
            ->where('is_published', true)
            ->first();
    }
}

Bind it in a service provider. Never let TomatoPHP\TomatoCms\Models\* leak into controllers or Blade beyond that boundary. It converts an existential dependency into an ordinary component swap.

Treat Filament resources as the editing surface, your Blade/Inertia as the reading surface. Let editors create and publish in the panel; render your own thin controllers calling ContentRepository on the front end. The package's shipped SEO routes are fine for a spike — but for production, owning the route contract keeps a package upgrade from silently changing your URLs.

Multilingual — verify the trait before writing locale-aware queries. Confirm whether translation is spatie/laravel-translatable under the hood (you get getTranslation()/setTranslation() and predictable fallback) or proprietary. If proprietary, read the trait end-to-end; fallback resolution is the thing that will bite you in a customer-visible way.

// Pattern: resolve a page in a locale with explicit fallback
$page = $repo->pageBySlug($slug, app()->getLocale())
    ?? $repo->pageBySlug($slug, config('app.fallback_locale'));
abort_unless($page, 404);

Cache the read side — it's the cheap win. pageBySlug(), menu trees, and settings lookups should be cached and invalidated on save. CMS read paths are trivially cache-friendly; the expensive mistakes are uncached menu trees firing on every request.

Cache::remember("page:{$slug}:{$locale}", 3600, fn () => $repo->pageBySlug($slug, $locale));
// Observer on save() → forget the tagged cache

Extend in your own namespace, never in vendor. Custom fields, workflow states, or extra Filament resources go in your own provider and your own namespace. Vendor edits are what make the eventual fork expensive.

Custom content types — lean on field definitions, don't invent a schema language. The package advertises flexible field definitions for custom content types. Use it for what it's designed for (blocks, structured metadata, repeatable fields). If you find yourself fighting the field system to model a real domain object, that's your signal to build a dedicated Eloquent model alongside the CMS rather than bending it.

Pin versions the moment the spike succeeds. composer require tomatophp/tomato-cms:1.x.x-style pinning (or a committed fork into your own namespace) keeps a stray composer update from pulling a breaking Filament major into prod.

Gotchas and Tips

  • Filament major alignment is a gate, not a detail. Last release is May 2024, predating several Laravel and Filament majors. Run composer why-not tomatophp/tomato-cms and read the lock diff. On a current stack, assume patching is required and budget the time.
  • composer require is a proxy decision. Adopting this is adopting the Tomato PHP admin ecosystem (admin, settings, roles, translations, media). Make that call explicitly with your team rather than sliding into a multi-package program through one innocent line.
  • Read migrations before running them. Table/column collisions with existing pages/posts/categories are the loud failure mode. Diff first.
  • Translation fallbacks fail silently. No exception, no log — just the wrong locale to a real user. Write a test that requests a page in a locale with no translation and asserts the expected fallback before you ship.
  • N+1 across translated fields and media relations. Eager-load translations and media; confirm slug, locale, and publish-status columns are indexed. Deep translation joins compound quietly over months.
  • Debugging reads: when a page 404s but exists in the panel, check publish flag, locale, and slug — in that order. The most common bug is publishing in one locale and requesting another.
  • Extension points to prefer: the service provider (register resources, migrations, routes), the config file (route prefix, locales, slug source), and an observer on the content models (cache invalidation, search indexing). Those three cover 90% of customization without touching vendor code.
  • No community safety net. GitHub issues on a 1-star repo won't get timely responses. Anything you don't understand during the spike, you own forever — write your read-through findings into a doc while the context is fresh.
  • Name a maintenance owner before the spike begins, with allocated capacity for dependency bumps and Filament patches. If nobody signs up, the honest output is a documented no-go — and you still walk away with the requirements artifacts to benchmark alternatives.
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