tomatophp/tomato-php
TomatoPHP core package gives Laravel developers a modular, open-source foundation for building admin panels and CMS applications, complete with ready-made CRUD generators, reusable plugins, and a growing ecosystem of tools for shipping dashboards faster. It is the heart of the TomatoPHP framework.
Architecture fit. This is a build-time code generator, not a runtime library, and that distinction drives every downstream decision. It scaffolds Laravel models, controllers, form requests, policies, routes, and Splade/Breeze views from an existing Eloquent model plus migration. Architecturally it fits one shape of product: conventional, model-centric CRUD behind an auth wall in a Laravel app already committed to Splade or Breeze. It is a poor fit for API-first services, event-sourced or CQRS domains, heavy multi-tenant permission matrices, or any front end on Inertia/React/Next. Note a scope ambiguity worth resolving before any commitment: the repository description advertises a "Full CRUD Generator for Splade & Breeze Starter Kit," while the package's own description positions it as the core foundation of the broader Tomato ecosystem — shared helpers, base classes, traits, and service-provider conventions. Those are two different products with two different blast radii. Confirm from php artisan list | grep -i tomato and vendor/tomatophp/tomato-php/src which one you are actually adopting.
Integration feasibility. Low friction mechanically: MIT-licensed, PSR-4, installs as composer require --dev tomatophp/tomato-php:^1.1.8, publishes config and stubs, and emits plain PHP your team owns. Prerequisites are modest but non-negotiable — Laravel 10/11, an already-running Splade or Breeze install, compiled Tailwind, and at least one existing model with a migration. The real feasibility question is not installation, it is review capacity: the package removes typing, not accountability. A generator without a named reviewer is a liability, not an accelerator.
Technical risk. The risk profile is dominated by one characteristic failure mode: the generator exits zero on broken output. Release 1.1.8 fixed emitted relation selects that called a non-existent ->remoteRoute('model.data') API instead of Splade's real ->remoteRoot('data') — a defect that passed type-checking, produced a plausible diff, loaded the page, and then 500'd when an admin opened a create form. The same bug shipped in a sibling package (tomatophp/tomato-plugins#7), which tells you the defect class is systemic to the namespace rather than a one-off. Secondary risks: overwrite-versus-skip semantics on existing files are undocumented and must be read out of the generator source (this is the difference between a convenience and a data-loss incident); silent skips on soft deletes, enum casts, translatable fields, and file uploads; standard generated-code defects left untouched by any release (open mass assignment, no per-action authorization, N+1 index queries, missing pagination); a single maintainer with 33 stars and 0 dependents; and release metadata containing dates ahead of normal review windows, so verify 1.1.8 is a real published Packagist tag before pinning.
Key questions. Which Breeze variant do we run — Blade, Livewire, or Inertia — and has our app already diverged from the generator's assumed conventions? Does the generator overwrite or skip existing files? Who is the named reviewer for every generated PR, and what checklist do they sign? How many resources in our backlog are genuinely conventional CRUD (the 5–50 band) versus differentiated work? What is our published-stub or fork story if we standardize? Can we keep this out of production images entirely?
Stack fit. Adopt only where the front end is already Splade or Breeze. Splade is the better-trodden path — the one publicly visible bug was a Splade bug that got fixed — so weight the pilot toward Splade unless the Breeze variant is confirmed and matched. Installation is require-dev only, excluded from production images; generated output is plain PHP committed to the app. That separation is what keeps removal to a git revert plus composer remove --dev.
Migration path. There is no migration — there is a generation event and then ownership. The sequence per entity is: make:model -m → write migration, relations, casts → run the generator → commit raw generator output as its own commit before any hand-editing → review pass → hand-fix → wire routes and nav → add policy and FormRequest tests → render every page once in a browser. Committing raw output first is not hygiene, it is the mechanism that makes later edits a readable diff rather than an ambush, and makes any re-run a reviewable comparison. Fence the blast radius by generating into a bounded namespace (app/Modules/Admin/… or a dedicated Admin directory) so generated-versus-handwritten is obvious in a diff and a bad run is one revert plus a directory delete. Retrofitting into a mature domain layer is where this goes wrong.
Compatibility. Pin tomatophp/tomato-php at 1.1.8 or later and pin it alongside Splade/Breeze versions — do not float either. The generator is coded against a snapshot of Splade's component API, and the 1.1.8 fix is precisely what that snapshot drifting looks like; a Splade minor bump can silently invalidate emitted components the same way. Keep the tomatophp/* constraint tight so transitive sibling packages can't drift in a composer update. Before evaluating anything generated on an older tag, run grep -rn "remoteRoute" app resources and remediate every hit.
Sequencing. Two stages, gated. Stage one is a 1–2 day sandbox spike: three entities — one trivial, one belongsTo + hasMany, one with a pivot and soft deletes — plus at least one relation select. Measure generated-versus-handwritten line ratio and log every defect, opening the log with "relation-select class" as row one. Stage two is a one-sprint pilot on internal or low-risk surfaces, measured on time to first working CRUD screen, boilerplate eliminated (target ~70%+), defects found in generated output, security findings, and ongoing maintenance effort. Write the kill criteria before the pilot starts, not after. Do not let a successful admin-panel pilot expand into a proposal to standardize across all product surfaces.
Maintenance. The team owns every generated line, its tests, its security posture, and its upgrades — the package owns nothing after generation. Generated output is a starting commit, not a managed artifact; never re-run against files you have since edited until overwrite semantics are confirmed from source. Expect an ongoing remediation tax from the four standard generated-code defects (mass assignment, missing per-action authorization, N+1 index queries, absent pagination/filtering) unless the review gate catches them. If we standardize, the durable extension point is publishing our own stubs or a small fork — far more stable than config flags, and it turns the package into a maintained internal asset rather than an upstream dependency.
Support. GitHub issues only, no SLA, one maintainer, 33 stars, zero dependents, no public production references. The escalation path is "read the stubs and fix it yourself," which is viable only because the output is plain PHP we own. That makes internal ownership the actual support model: name an owner, document the spike findings, and write the removal recipe (git revert + composer remove --dev) into the notes — otherwise the knowledge walks out with whoever ran the spike.
Scaling. Scales horizontally with the CRUD backlog, not with product ambition. It is worth real capacity on repetitive admin surfaces; it is worth nothing on differentiated work. The right reallocation is to deprioritize in-house generic CRUD scaffolding and redirect that capacity to permissions and audit trails, complex workflows, integrations, domain logic, and customer-facing experience — that reallocation, not the generator, is where the ROI lives. Note the ceiling: value collapses to near zero the moment front-end conventions diverge from Splade/Breeze, and there is no predictable framework-major upgrade story for a multi-year commitment.
Failure modes. Ranked by likelihood and cost:
remoteRoute, check the JSON endpoint returns a data envelope, and confirm auth/middleware isn't 302-ing to login (which renders as an innocuous empty dropdown).composer update — tight tomatophp/* constraints.Ramp-up. Low learning curve for any Laravel-fluent engineer, front-loaded into about an hour of reading: list the real commands and publish tags, publish and read the stubs (the stubs are the product — controller shape, FormRequest presence, policy wiring, and index eager-loading are all decided there), then read the generator class to answer the overwrite question and the relation-inference question. Encode the review as a PR template with seven fixed checks — mass-assignment guards, a policy or gate per action, FormRequest validation emitted and used, eager loading on index, pagination present, middleware/CSRF wiring, and every page rendered once — because generated diffs get rubber-stamped precisely when they look plausible. Treat bundled global helpers as a bonus utility belt for trivial boilerplate only; do not build domain logic on them, as they are the least portable artifact if the package is removed.
How can I help you explore Laravel packages today?