Before writing any code, confirm the package still boots on your Laravel version — the repository is archived (last maintained state), 6 stars, zero dependents, so treat it as vendored code you own rather than a dependency you consume. Run php artisan list | grep -i addon after install to discover the actual commands and namespaces; the README is thin and the source is the real documentation.
Minimal path:
composer require jumilla/laravel-addomnipot (or fork it into packages/ and add a path repository — recommended given the archived status).php artisan vendor:publish --provider="Jumilla\Addomnipot\...ServiceProvider". Read the published config/addomnipot.php (or similarly named) for the addon root path and manifest settings.php artisan about or php artisan package:discover).addon:make, make:addon, etc. — check artisan list), then inspect the generated directory: this tells you the expected layout (provider, routes, views, migrations, addon.json/manifest).First real use case: take one self-contained feature already in your app — a small admin report, a webhook receiver, a settings panel — and move it into an addon. That exercises discovery, provider registration, routes, views, and migrations in one pass without risking core code.
Addon-as-feature-module. Use addons to isolate optional or customer-specific features: billing, a particular integration, a tenant skin. Keep core domain models in the app; keep addon code dependent on core, never the reverse.
Manifest-driven discovery. Each addon typically declares itself via a manifest (addon.json / composer.json extra) plus a service provider. In day-to-day work, the pattern is: addon directory → manifest → provider → routes/views/migrations registered from that provider. When something "isn't loading," the manifest is almost always the culprit.
Lifecycle hooks. The value of an addon system is install/enable/disable/uninstall as distinct operations. Wire your own conventions around them:
// inside the addon's service provider
public function register() { /* bind interfaces, merge config */ }
public function boot() {
$this->loadRoutesFrom(__DIR__.'/routes/web.php');
$this->loadViewsFrom(__DIR__.'/resources/views', 'billing');
$this->loadMigrationsFrom(__DIR__.'/database/migrations');
$this->mergeConfigFrom(__DIR__.'/config/billing.php', 'billing');
}
Make install idempotent (publish assets, run migrations, seed defaults) and make uninstall conservative (never drop tables automatically; require an explicit --purge).
Feature gating. Treat "enabled addons" as a runtime check, not a build-time one. Wrap routes in middleware that 404s when the addon is off, and guard Blade sections:
@if(addon_enabled('billing'))
@include('billing::widget')
@endif
If the package exposes no helper, write a thin AddonManager facade wrapper so your app never calls package internals directly — that isolation is what lets you swap or fork the package later.
Namespacing everything. Routes names (billing.), view namespaces (billing::), config keys (billing.), translations (billing::), and DB table prefixes. Collisions are the number one source of "works locally, breaks in prod" in addon systems.
Per-environment / per-tenant enablement. Store enabled addons in config for single-tenant apps, but in the database when tenants differ. Cache the enabled list and bust it on enable/disable — do not hit the DB on every request.
Testing. Test addons as if they were separate packages: a test case that boots only the addon provider, with an in-memory SQLite DB running just that addon's migrations. This catches missing loadMigrationsFrom and hardcoded paths early.
README explaining the fork and why.php artisan config:clear && php artisan route:clear && php artisan view:clear. If the addon list is cached in the package's own store, look for an addon:refresh/addon:cache command. In production deploys, always clear before rebuilding caches.composer dump-autoload. If you generate addons in CI, make that part of the deploy script.public/vendor/... with the same filename silently overwrite. Use vendor/<addon-name>/ as the publish tag target.route:cache fails in production. Use controller arrays exclusively.composer dump-autoload → caches cleared → provider registered (dump app()->getLoadedProviders() in tinker).How can I help you explore Laravel packages today?