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

tomatophp/tomato-sections

Laravel package for TomatoPHP that lets you create and manage dynamic page sections from the admin panel. Build reusable content blocks with customizable views, ordering, and localization—no code changes needed to update site layouts.

View on GitHub
Deep Wiki
Context7

Getting Started

tomato-sections is the dynamic-content/page-section layer of the TomatoPHP admin stack — an admin-managed table of named sections (each with a key and a structured value payload) that your Blade views read back at render time. Treat it as a lightweight, developer-defined CMS, not a free-form page builder.

Minimum path to a working install:

composer require tomatophp/tomato-sections
php artisan tomato-sections:install
php artisan migrate

The install command publishes the config, migration, and (depending on flags) seeds demo data — check the README for your version's --with / --without options before running it on an existing database, since it may also touch tomato-admin prerequisites.

Where to look first, in this order:

  1. config/tomato-sections.php — table name, route/middleware, and any resource registration flags.
  2. The published migration — column names and the type of the value column (JSON vs longText determines whether you cast to array or store raw HTML).
  3. app/Filament/**/SectionsResource (or the vendor resource, if not published) — this is where fields, form schema, and table columns are defined.
  4. The package's Blade/view directory — that's what changed in v1.0.6 (RTL + dark mode markup).

First real use case: a marketing homepage whose hero copy, CTA labels, and footer blurb are editable without a deploy.

// app/Models/Section.php (or use the vendor model directly)
$hero = \TomatoPHP\TomatoSections\Models\Sections::where('key', 'home.hero')->first();
{{-- resources/views/sections/home/hero.blade.php --}}
@php($hero = $sections['home.hero'] ?? null)
@if ($hero)
    <section class="hero">
        <h1>{{ $hero['title'] }}</h1>
        <p>{{ $hero['subtitle'] }}</p>
        <a href="{{ $hero['cta_url'] }}">{{ $hero['cta_label'] }}</a>
    </section>
@endif

The upgrade from v1.0.5 → v1.0.6 is UI-only: dark mode and RTL styling in the admin resource. No API, schema, or method signature changes are advertised, so nothing in the above needs to change.

Implementation Patterns

Resolve sections once, render many. Don't hit the database inside a loop of partials. A small view composer or a single service call keeps N queries from becoming N×M:

// app/Providers/ViewServiceProvider.php
View::composer('sections.*', function ($view) {
    $view->with('sections', Cache::rememberForever('sections.all', fn () =>
        \TomatoPHP\TomatoSections\Models\Sections::pluck('value', 'key')->toArray()
    ));
});

Flush that key from the model's saved/deleted events so editors see changes immediately instead of waiting for a TTL.

Key namespacing is the whole contract. Because views look sections up by key string, adopt a convention from day one — {page}.{slot} (home.hero, pricing.faq) or {feature}.{slot}. This lets you group, filter, and later swap a section's implementation without touching the admin UI. Renaming a key in the admin panel silently blanks a live page; a namespaced key at least makes the breakage obvious in the resource list.

Seed keys, don't create them in production. The section's schema (which keys exist, which sub-keys each expects) belongs in a seeder so every environment — local, staging, CI — starts with the same set. The content stays admin-editable.

// database/seeders/SectionSeeder.php
Sections::updateOrCreate(['key' => 'home.hero'], ['value' => [
    'title' => 'Welcome',
    'subtitle' => '',
    'cta_label' => 'Get started',
    'cta_url' => '/register',
]]);

Cast and validate the payload. If value is JSON, cast it to array on the model and validate sub-keys on save with a Filament form schema. Without a declared shape, an editor can add a field that your Blade silently ignores — or worse, remove one that your view dereferences unguarded. Use data_get($hero, 'cta.label') with a default rather than $hero['cta']['label'].

Localisation: one key per locale, not one JSON per locale. Storing home.hero.en / home.hero.ar as separate rows keeps the admin UI, the cache key, and your lookup logic simple ("home.hero.{$locale}", falling back to the default locale).

Compose with the rest of the Tomato stack rather than duplicating it. tomato-category for taxonomies attached to sections, tomato-forms for embedded forms referenced by ID inside a section payload, tomato-settings for global values that shouldn't be per-section. Resist storing relational data inside the value blob — IDs and slugs only.

RTL and dark mode (new in v1.0.6). The admin resource now honours Filament's panel-level theming. To actually see it:

->darkMode(true)
->defaultThemeMode(ThemeMode::Dark)
// and for RTL, a panel locale list including e.g. 'ar' with RTL direction
->locales(['en' => 'English', 'ar' => 'العربية'])

These are admin-panel concerns. If your front end also needs RTL, that's your own CSS — the package only styles its resource. Use logical properties (margin-inline-start, padding-inline-end) in any custom section views so a single stylesheet works in both directions, and prefer CSS variables / dark: variants over hardcoded hex values so editor-entered content doesn't break in dark mode.

Gotchas and Tips

  • Section output is raw HTML. The value field is typically edited through a rich-text/editor field, so printing it requires {!! !!}. That makes the admin panel a stored-XSS surface if any non-trusted role can edit sections. Gate the resource with a policy and sanitise on render (HTMLPurifier or a strict allow-list) for anything user-facing.
  • Re-publish views after upgrading. v1.0.6 ships new markup for RTL/dark mode. If you previously ran vendor:publish --tag=tomato-sections-views and edited the files, your copies won't receive the fix — diff the published views against vendor before assuming the upgrade "didn't work." Same for any published CSS assets; re-run the publish/asset step after composer update.
  • Upgrading 1.0.5 → 1.0.6 is low-risk but not zero-risk. No migrations or API changes are announced, so this is a safe patch. The realistic breakage is self-inflicted: an overridden resource view or custom CSS that assumed the old LTR/light-only class names. Test your admin panel in dark mode and in an RTL locale before rolling to production.
  • Cache invisibility is the #1 debugging trap. "I saved it and the page didn't change" is almost always a section cache without invalidation, or an OPcache/route cache layer on top. Verify against the database (Sections::where('key', ...)->first()) first, then bisect the cache layers.
  • Type drift on JSON columns. MySQL JSON columns reject invalid JSON silently in some paths and loudly in others; longText columns store whatever the editor posts. Pick one storage type, cast it consistently, and add a array cast in a custom model if you've extended the vendor one — otherwise $section->value returns a string on one environment and an array on another.
  • Multi-panel installs. The resource auto-registers into the default Filament panel. If you run separate admin/app panels, register the resource explicitly only where editors belong, and check the config's register_navigation-style flag if the menu item appears twice.
  • Extension point worth knowing: overriding the Filament resource (subclassing SectionsResource and pointing config at your class) is cheaper than forking the package — you keep the RTL/dark-mode upstream improvements in v1.0.6 while customising form fields and table columns.
  • Permissions. Wire the resource into tomato-roles/Filament shield early. Section editing is effectively "edit the website," so it should not inherit the default authenticated-admin policy.
  • Testing. Sections::factory()->create(['key' => 'home.hero', 'value' => [...]]) plus a view assertion is enough to cover the render path; also assert the graceful-empty case ($sections has no matching key), because that's the failure mode a key rename produces in production.
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.
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings
coleus/apps
seampact/laravel
emmyconceptng/laravel-ng-postcode
jjsquady/round-robin
skyraptor/chatter
edlugz/sasapay
abbasudo/artisan-route