tomatophp/tomato-menus
Menu management for Laravel and TomatoPHP: build database-backed navigation menus, organize nested items, and render dynamic links in admin panels or Blade views with a simple, reusable API.
Architecture fit
tomatophp/tomato-menus is a Laravel package that supplies three things: a menu data model (nested items, multiple named locations), a dual storage driver (JSON file or database table), and a Filament admin surface for editing menus at runtime. It assumes a conventional Laravel app — service provider registration, published config, published migrations, Blade rendering. It does not impose an application architecture, which is good for fit and bad for leverage: you get a menu CRUD layer, not a navigation platform.
The genuinely product-relevant architectural feature is the JSON-vs-database driver split, because it silently assigns operational ownership. JSON-backed menus ship with a release: code-reviewed, diffable, revertible. Database-backed menus are editable by marketing/ops at runtime without a deploy. Any TPM adopting this should force that decision per navigation surface up front — primary nav (JSON, engineering-owned), sidebars/footer/campaign links (DB, business-owned). If the package only supports a global provider selection rather than per-menu overrides, that is a real limitation and should be confirmed during the spike, not after.
Fit is strongest when the application is already inside the TomatoPHP/Filament ecosystem. Outside it, the Filament plugin, the config conventions, and the shared TomatoPHP assumptions become integration surface rather than convenience.
Integration feasibility
Mechanically straightforward: composer require, publish config, publish and read the migration, optionally register the Filament plugin, render a menu in a Blade view. The recommended pattern is to wrap it behind an internal interface on day one — something like MenuRepository::tree(string $key): array — with all views and controllers depending on that interface rather than the package namespace. That wrapper is the single highest-value integration decision, because it converts vendor risk into a roughly two-day replacement cost (swap the implementation for spatie/laravel-menu or a hand-rolled menus/menu_items pair).
Rendering should also be owned: build a recursive Blade partial over the normalized tree, with your markup, classes, aria attributes, and active-state logic. Treat any package-provided Blade component as a reference implementation. This keeps design and accessibility changes from requiring forks of vendor views.
Feasibility caveats: documentation is effectively absent, so integration cost is dominated by source reading rather than wiring. Budget the spike accordingly.
Technical risk The adoption signals are weak and should be stated plainly to stakeholders: zero stars, zero dependents, single-author ecosystem, one release in May 2024, MIT license. That is not a disqualifier for a low-stakes internal surface, but it is a standing vendor risk on anything customer-facing.
Risk concentrates in four places:
saved/deleted. A stale menu is silent and customer-facing.composer.json before trusting it, and never adopt in the same release window as a framework upgrade.Key questions
storage/ in containers, and admin edits diverging silently from the repo.is_active flag we must filter with a Gate in the wrapper?v1.1.8, and budget roughly a day per quarter for compatibility churn?Stack fit Best fit: Laravel 11 / Filament v3 applications, TomatoPHP-aligned codebases, internal admin panels, back-office tooling, and MVP marketing sites where menus are supporting infrastructure. The Filament admin surface comes largely for free in that context, which is where the sprint-day savings actually materialize.
Poor fit: applications outside the TomatoPHP ecosystem, apps with strict dependency governance that a single-maintainer package will not clear, or any stack where navigation is a revenue-bearing surface.
Migration path There is no migration path in the data sense — this is greenfield adoption into a package with no prior installed base and no documented upgrade history. The relevant "migration" is the exit path, and it should be planned before integration begins:
DatabaseSeeder read them into rows so a fresh environment boots with correct navigation. That gives you code-review benefits of JSON plus runtime editability via DB.The stop-and-build threshold should be agreed in advance: a menus + menu_items pair is roughly three to five days to build, fully owned. The package's value is convenience, not capability — worth about one sprint-day of integration savings, not a standing vendor risk on our navigation surface.
Compatibility
Pin the constraint to the exact v1.1.8 line. Do not float a zero-maintainer package. Verify the declared Laravel and Filament constraints in composer.json against the application's versions, and confirm the Filament major matches before registering the plugin in a PanelProvider. Verify the vendor publish tags against the provider rather than trusting documentation, since documentation does not exist. The package's Blade helpers and any package Blade components should be treated as advisory; our own partial is the production renderer.
Sequencing
composer.json, the service provider, the config, and the migrations in that order. Render a two-item menu end-to-end via the JSON driver. Create a parent with children, delete the parent, assert the child behavior. Confirm cache behavior on save.MenuRepository::tree($key), implement it, build the recursive Blade partial, add the cache-invalidation observer and the Gate-based visibility filter.This belongs in the MVP or internal-tooling phase. Adopting it late, onto a mature app with tenancy or permissions requirements, invites schema work that should have been a day-one decision.
Maintenance
Assume we are the first real adopter and tool accordingly. Mirror the repo internally (private VCS or path repository with an upstream remote), keep our patches as one commit per fix so upstream merges remain possible if the maintainer revives the project, and budget roughly one day per quarter for Laravel/Filament compatibility churn. That budget is not optional — if it is not allocated, the integration savings are illusory and the package becomes deferred cost.
Our own maintenance surface is small if disciplined: the wrapper interface, the Blade partial, the cache observer, and the visibility filter. Everything else should stay inside the three extension points.
Support There is no vendor support, no SLA, no security review process, and no community to speak of — zero stars and zero dependents means no de facto peer support either. Support is entirely internal: whoever integrates it owns it. That is acceptable for internal admin navigation and unacceptable for anything with an enterprise support requirement or a customer-facing revenue dependency. This must be stated explicitly rather than implied, so that no stakeholder later assumes an escalation path exists.
Scaling Technical scaling is not the concern — menus are small, cacheable, and resolved once per request. Resolve each menu key once, normalize to an array, cache it, and invalidate on save. The scaling questions that matter are organizational:
order is not stable. Worth an explicit test if multiple editors are expected.Failure modes Ranked by business impact:
Establish a debugging order and document it: (1) is_active, (2) parent reachable in the tree, (3) wrapper cache stale, (4) visibility/Gate filter hiding it, (5) provider switched. Expose the raw normalized tree on a debug route — that collapses most investigations to a minute.
Ramp-up Low for anyone comfortable with Laravel and Filament: the concepts are a tree, a driver switch, and a Blade render. The real ramp-up cost is discovery, because there is no documentation — the first engineer must read the source, and that knowledge should be captured as internal notes (schema behavior, provider semantics, cache behavior) rather than rediscovered.
Recommended onboarding sequence for the team: read the wrapper interface and Blade partial first, not the package; the package is an implementation detail behind them. New engineers should never need to open vendor code to change navigation. That is the operational test of whether the wrapper was worth building — and it is the same test that makes the exit cheap if upstream goes quiet.
Decision summary for stakeholders. Green-light for internal admin panels, back-office tools, MVP marketing sites, TomatoPHP/Filament-aligned apps, and JSON-seeded default navigation, with a private mirror, a pinned version, a wrapper interface, owned rendering, and a budgeted quarterly compatibility allowance. Block for multi-tenant CMS navigation, approval or versioning workflows, personalized or A/B-tested menus, headless delivery, enterprise support requirements, and any surface where a stale menu is a revenue event.
How can I help you explore Laravel packages today?