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

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.

View on GitHub
Deep Wiki
Context7

Getting Started

Confirm the stack first. This package is only worth wiring up if you are on Laravel + Filament and intend to ship features as discrete togglable modules. Check the tagged composer.json for the supported Laravel/Filament majors before you start — the Filament admin screens are the headline feature, and a Filament major mismatch leaves you with a thin provider loader and no reason to use this over plain Composer packages.

Install pinned.

composer require tomatophp/tomato-plugins:1.2.6

Do not use a caret constraint. This is a pre-2.0, single-maintainer package with essentially no downstream usage; an unpinned minor bump silently changing provider registration or discovery behavior is the exact failure mode you cannot afford.

Publish and migrate.

php artisan vendor:publish --provider="TomatoPHP\TomatoPlugins\TomatoPluginsServiceProvider"
php artisan migrate

If the provider FQCN differs in the tagged source, run php artisan vendor:publish interactively and pick the Tomato Plugins tag, then php artisan list and grep for plugin and tomato to enumerate the actual commands. The published config is the primary contract — read it before writing any plugin code.

Where to look first, in order:

  1. config/tomato-plugins.php — plugin paths, base namespace, enabled-state storage. Everything else keys off this.
  2. The package's service provider register()/boot() — this is the discovery mechanism. Read it, don't assume it. You need to know whether discovery is filesystem-scan, Composer-installed-package scan, or config-driven; it determines your deployment model.
  3. The Filament plugin-manager page/resource — what an operator actually sees when they toggle a module.
  4. The published stubs directory — this defines the shape of a plugin better than any README.

First use case. Build one throwaway end-to-end plugin: scaffold it, add a migration with a prefixed table, register one Filament resource, add one route and one translation key, then enable it from the admin panel and confirm the resource appears. That single loop exercises discovery, migration loading, asset publishing, and Filament registration — the four things most likely to break. Do it on a scratch database before you touch any real feature.

Implementation Patterns

One plugin = one Composer package, even locally. Use a path repository during development and a private registry (or image-baked plugin directory) for delivery. The plugin must be Composer-autoloadable before discovery can find it; runtime toggling of filesystem-adjacent code is a second lifecycle layered on top of Composer's, and conflating the two is where plugin systems rot.

"repositories": [{ "type": "path", "url": "plugins/*" }]

Plugin directory shape. Keep it conventional and boring — provider at the root of the plugin namespace, database/migrations, routes/, resources/views, resources/lang, src/Filament/Resources. Resist per-plugin creativity; the discovery layer expects conventions, and every deviation is a future debugging session.

Gate on enabled state, don't rely on it. Treat "enabled" as a feature flag consulted in the plugin's own provider:

public function boot(): void
{
    if (! PluginManager::isEnabled('billing-addon')) {
        return;
    }

    $this->loadMigrationsFrom(__DIR__.'/../database/migrations');
    $this->loadRoutesFrom(__DIR__.'/../routes/web.php');
}

This keeps the plugin inert even if the manager misreports state, and it makes the enable/disable semantics legible to the next developer.

Register the manager in your panel provider. The admin UI does not appear automatically:

public function panel(Panel $panel): Panel
{
    return $panel
        ->plugin(TomatoPluginsPlugin::make());
}

Enable/disable from the deploy script, not the UI, in production. Point-and-click enablement means production composition is not reproducible from the repository. Wire an Artisan call into your deploy pipeline (php artisan plugin:enable billing-addon — confirm the exact signature from artisan list) and run migrations in the same step. Use the admin UI for staging and evaluation only.

Wrap the toggle surface. Define your own two-method interface and adapt the package behind it:

interface PluginRegistry {
    public function isEnabled(string $plugin): bool;
    public function enable(string $plugin): void;
}

The cost is an hour. The payoff is that migrating to nwidart/laravel-modules or a hand-rolled loader touches one file instead of every feature.

Version the plugin contract. If you have multiple teams writing plugins, document the base provider class, the expected directory layout, and the table-prefix rule. Adopting a plugin architecture is a standing commitment to keeping that surface stable.

Gotchas and Tips

The supplied release note does not describe this package. v1.2.6 is documented as a fix for generated relation selects using ->remoteRoot('data') — that is form/relation generation behavior from a sibling Tomato package, not plugin installer behavior. Diff the tagged release against the docs before scoping anything. A documentation mismatch in a package that controls provider registration is a hidden-behavior risk, not a cosmetic one.

Disable ≠ uninstall. Expect disabling a plugin to stop registration but leave migrations applied and tables populated. Decide explicitly, per plugin, whether disable means "hidden from UI," "routes 404," or "data archived," and write it down. Do not assume rollback happens.

Cache interactions are the classic break point. Dynamic provider registration must survive php artisan config:cache, route:cache, and optimize. Add a CI job that runs a full optimize → enable → migrate → disable → rollback cycle against a scratch database. If your plugin routes are registered conditionally on enabled state, route:cache will bake in whatever was enabled at cache time — clear and rebuild route caches on every enable/disable transition.

Multi-node and read-only filesystems. Plugin discovery that scans directories behaves differently when the artifact is baked into an immutable image and only some nodes have the plugin directory. Verify discovery on a second node before you promise anything, and ensure published assets land on shared storage, not a per-container public/.

Run composer dump-autoload after adding a plugin. Discovery cannot see a class Composer does not know about. This is the single most common "the plugin won't register" cause, and it presents identically to a genuine discovery bug.

Prefix every table and config key. Two plugins both creating settings or both reading config('tomato-plugins.x') will collide silently. Establish a prefix convention at plugin #1, not plugin #6.

Keep plugin state reproducible across environments. Determine whether enable/disable is persisted in the database or in config. If it is the database, your dev → staging → prod promotion needs a seed or a deploy-time enable step, or environments will drift and "works in staging" becomes unfalsifiable.

Trust boundary. This is a first-party module system, not a sandbox. If any code path downloads or installs plugins from a remote source, that is a supply-chain and privilege boundary problem requiring review before non-engineering users get a button. Do not present it as marketplace safety.

Debugging checklist when a plugin does not appear: composer dump-autoload → is it in the configured plugin path → is it reported as enabled → clear config/route/view caches → check whether the panel provider registers the manager plugin → check whether the resource is registered on the correct panel ID (multi-panel apps fail here frequently).

Verify the release resolves. The listed last release date is in the future relative to normal consumption; confirm the version actually resolves from Packagist and that the git tag matches the changelog before pinning.

Plan for a fork. One star, zero dependents, single maintainer. Pin the version, keep the abstraction thin, and budget the fork as a plausible outcome rather than a contingency.

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.
diephp/sequences
users-au/socialite-provider
twidpay/twid-logger
mrnewport/laravel-plaid
mrnewport/laravel-repo
mrnewport/laravel-docsign
mrnewport/laravel-stow
mrnewport/laravel-flow
mrnewport/laravel-priceable
mrnewport/laravel-nestedterms
fredmeng/laravel-apidoc-generator
itstructure/laravel-multilingual-tools
pdustdar/elasticsearch
markupai/php-sdk
rlacerda83/lumem-microservice-shipment
uni-method/symfony-lib
tacticmedia/scim-server-bundle
neika-ai/symfony-bundle
mattleads/agent-publishing-bundle
glavweb/framework-bundle