tomatophp/tomato-plugins
Tomato Plugins is a Laravel plugin manager for the TomatoPHP ecosystem, letting you install, enable, disable, and organize modular plugins from a single dashboard so you can extend your application with reusable features without touching core code.
Architecture fit. tomatophp/tomato-plugins is an application-composition layer, not a feature library: it discovers plugin service providers, boots their migrations, routes, views, translations, and Filament resources, and exposes per-plugin enable/disable through the admin panel. From a TPM standpoint that is the right layer to own if the product strategy depends on shipping variable feature sets from a single core. It is the wrong layer to own if the roadmap is a single-deployment product with one feature set — in that case we are buying a second lifecycle for code (runtime toggling) on top of Composer's, and inheriting the governance burden that comes with it. The leverage is entirely conditional on a multi-variant delivery model: tiers, per-client add-ons, per-tenant capability sets, or several internal teams contributing self-contained features to one admin shell.
Integration feasibility. Installation is a non-event — Composer install, publish config for plugin paths and namespaces, register the provider, let discovery take over. The TPM-relevant work is the surrounding decisions, not the code: where plugin source lives, how plugin versions are pinned, who holds the authority to enable a module in production, and what "disabled" means for data written by a plugin that was previously on. That last one is a product decision disguised as an engineering question, and it should be answered in writing before the pilot starts, because it determines whether "off" is a reversible feature flag or a one-way door.
Technical risk. The signals are weak and should be treated as such in any business case: one GitHub star, zero dependents, a pre-2.0 version line, and a single-maintainer profile. The blast radius is disproportionate to that maturity — this package mutates how the application assembles itself, so a defect or an unannounced behavior change surfaces as a broken deployment rather than a broken widget. Two specific diligence items stand out. First, the supplied v1.2.6 release note describes relation-select/form generation behavior (->remoteRoot('data')), which has nothing to do with plugin installation; either release artifacts are shared across a package family or the documentation is inaccurate, and neither is acceptable in a component that controls provider registration. Second, the listed release date (2026-10-14) is anomalous relative to normal consumption cadence — confirm the tag actually resolves from Packagist and that the source we review is the source we would install. Beyond that: runtime mutation of app state creates config drift and non-reproducible production; auto-discovered migrations and assets are a known failure point under config:cache, route:cache, and optimize; and if plugin installation can fetch or execute external code, we have inherited a supply-chain boundary that must be reviewed before any non-engineering user touches the toggle.
Key questions.
composer.json or only implied?Stack fit. Adopt only behind an explicit stack commitment: Filament for back-office surfaces, and a genuine expectation of repeatable multi-module delivery. The value is concentrated in the Filament admin integration — plugin management screens, resource auto-registration, per-plugin settings. If we are on an incompatible Filament major or not on Filament at all, we are left with a thin provider loader and should use plain Composer packages with explicit registration instead. If the actual need is package organization rather than runtime toggling, nwidart/laravel-modules or hand-rolled service providers give the same modularity with a far larger validation base; we should be honest in the decision doc about which problem we are solving.
Migration path. Treat this as greenfield placement, not a retrofit. Introducing a plugin loader into a mature monolith is a much larger change than the install instructions imply, because it asks existing core features to move behind a boundary they were never written against. The defensible sequence is: pick a new or non-critical service, or extract exactly one leaf feature from the existing app, and prove the full loop — discovery, migration, asset publish, Filament resource, runtime toggle, toggle back. If we are already on Tomato PHP components, align with their conventions so plugin definitions match sibling packages; if not, wrap the enable/disable and discovery surface behind an internal PluginContract we own, so a swap to a maintained alternative is a contained change rather than a re-architecture.
Compatibility. Pin the exact version. Lock Laravel and Filament majors in composer.json. Put four checks in CI before the pilot is declared a success: clean install from a fresh checkout; php artisan optimize followed by plugin enable and disable; migrate → rollback on a scratch database; and asset publishing on a read-only filesystem. Confirm the package's config keys and plugin namespaces do not collide with existing application config.
Sequencing. Stage the decision, don't take it in one step. (1) Diligence, ~2 days: resolve the documentation mismatch against tagged source, confirm the support matrix, check Packagist resolution and maintainer responsiveness. (2) Proof of value, ~1–2 weeks: build one real add-on as a plugin and measure the concrete delta — lines of core code touched, branching avoided, time to ship a variant. (3) Hardening, ~1 week: internal wrapper, CI gates, a written policy on who may toggle what. (4) Decision gate: standardize, fork, or stop. Budget the whole thing as a spike with a kill criterion, and make the kill criterion numeric before starting — e.g. "if the add-on requires more than N lines of core change, we stop."
Maintenance. Adopting this is a standing roadmap commitment, not a one-time install. A plugin architecture implies an API contract, a versioning and deprecation policy, and upgrade guides — we now own keeping plugin interfaces stable across Filament majors. The realistic run rate is ongoing small maintenance plus a concentrated upgrade cost at each Filament release. Pin versions, treat every Filament major as a scheduled project, and assign a named owner for the plugin contract; the failure mode here is an unowned abstraction that quietly rots until a security patch forces a Filament upgrade we cannot absorb.
Support. There is no vendor, no SLA, no security review artifact, and no deprecation policy — the MIT license is the entire guarantee. Support is therefore self-support: we must be able to read the source, patch it, and fork it. That is acceptable for an internal platform capability and unacceptable for anything customer-facing under contractual uptime. Practically: maintainers should assume they will read the discovery code at least once, and the decision record should state explicitly that forking is inside the plan rather than a fallback.
Scaling. Two dimensions matter. Technically, plugin loaders break first on multi-node topology and read-only filesystems — discovery that depends on writing caches or on local filesystem state behaves differently on a laptop than across a fleet, and that must be validated before we plan to scale out. Organizationally, the model scales well: it is precisely the mechanism that lets several teams contribute self-contained features to one admin shell without merge contention. That is the strongest argument for adoption and should be the metric the pilot is judged on — reduction in cross-branch conflict and per-client fork count, not code elegance.
Failure modes. The ones to plan for: (a) a plugin enabled in production that is not enabled in staging, producing config drift that is invisible in the repo — mitigate with DB-tracked state and a reconciliation command; (b) orphaned tables after disable, which makes "off" irreversible and should be documented as a known limitation rather than discovered during an incident; (c) cache-busting failures after deploy where a stale config:cache leaves routes or resources unregistered; (d) a Filament major upgrade silently breaking plugin discovery. Each has a cheap mitigation: reconciliation command, explicit data-retention policy, post-deploy verification in CI, and a compatibility matrix in the runbook.
Ramp-up. Budget roughly half a day for an engineer to follow the docs and scaffold a plugin — the Artisan scaffolding is the easy part. Budget materially more for the team to internalize the conventions: what belongs in a plugin versus core, how plugin migrations are versioned, how settings are surfaced, how a plugin is tested in isolation. That written convention document is the real deliverable of the pilot and should be treated as such. For PM and stakeholders, the ramp-up is conceptual, and it is the highest-leverage work: the org must understand that "disabled" is an operational state, that plugin composition is not reproducible from the repository alone, and that this is a first-party trusted-module system rather than a sandbox — do not let it be pitched internally as a marketplace or third-party extension mechanism until both the spike and the maintainer-health check come back green.
How can I help you explore Laravel packages today?