catchadmin/plugin
Plugin manager for the CatchAdmin admin framework built on Laravel, providing a modular system to package, install, enable, and remove admin extensions. Handles plugin discovery, configuration, and lifecycle hooks so custom backend features can be added or dropped without touching core code.
Install the package via Composer from your CatchAdmin application root:
composer require catchadmin/plugin
Then publish and run its migrations so the plugin registry table exists:
php artisan vendor:publish --provider="CatchAdmin\Plugin\PluginServiceProvider"
php artisan migrate
The plugin manager registers a route group under the admin panel (typically /admin/plugins). Log in as an admin, and you should see a plugin list with install/enable/disable/uninstall actions. This UI is the primary surface — it drives the lifecycle operations that the package performs programmatically.
Where to look first:
config/plugin.php (or config/catch.php depending on the merged config) — this defines the plugin root path, the manifest filename, and the namespace prefix. Almost every customization decision lives here.plugins/ at the project root or modules/ under app/) — each subfolder is a plugin.Plugin model / repository — it tracks name, title, version, status, and installed_at.First use case: Scaffold a trivial "hello world" plugin, enable it from the admin UI, and confirm a route it registers resolves. This validates the entire pipeline (autoload, provider boot, route registration) before you invest in a real feature. If you have an artisan generator (many versions ship php artisan plugin:make Name), use it — hand-rolling the folder structure is where most first attempts fail.
Manifest-driven plugins. Each plugin is defined by a manifest (usually plugin.json or info.php) that declares the plugin name, title, description, version, author, and — critically — the namespace and service provider. The manager reads this to render the list and to decide what class to boot. Treat the manifest as the source of truth; the database row is a cache of its status.
Service provider as the integration point. A plugin typically contains a Providers/PluginServiceProvider that registers routes, views, translations, and migrations. Follow the standard Laravel package conventions inside it:
public function boot()
{
$this->loadRoutesFrom(__DIR__.'/../routes/admin.php');
$this->loadViewsFrom(__DIR__.'/../resources/views', 'myplugin');
$this->loadMigrationsFrom(__DIR__.'/../database/migrations');
$this->publishes([...], 'myplugin-assets');
}
The plugin manager decides whether to register this provider based on the plugin's enabled status, so never assume your boot() runs — always guard side effects (migrations, config writes) behind install/enable hooks rather than boot().
Lifecycle hooks. The manager exposes install / enable / disable / uninstall events. Common pattern: put data-initialization logic (seed permissions, create menu entries, publish assets) in an install() method or an install-time migration rather than in a service provider. Enabling a plugin is usually cheap (flip a status and re-register providers); installing is the expensive, one-time operation.
Menu and permission integration. In CatchAdmin-flavored plugins, you'll usually register admin menu items and permission slugs on install. Do this by inserting rows via the CatchAdmin menu model inside your install routine, and delete them on uninstall. If you skip cleanup on uninstall, the admin menu accumulates dead links — a very common complaint.
Composer autoload for plugin namespaces. If plugins live outside app/, you need the composer autoloader to know about them:
"autoload": {
"psr-4": { "Plugin\\": "plugins/" }
}
Then composer dump-autoload after creating a new plugin. Some setups rely on the plugin manager dynamically adding autoloaders at runtime — if so, don't rely on that in tests or console commands where the manager may not boot.
Version-gated plugins. If you ship plugins that must match the host app version, read the manifest's require/version field and refuse install when the running CatchAdmin version doesn't satisfy it. Better to fail fast at install than to boot a broken provider.
php artisan cache:clear and sometimes php artisan config:clear. If a plugin "won't show up," clear caches before touching code.composer dump-autoload after every new plugin folder. Namespace-not-found errors on a fresh plugin are almost always an autoloader problem, not a manager bug.boot(), not at the top of the provider file.composer.json and vendor-review the PluginManager class before relying on lifecycle guarantees in production.try/catch and log to a dedicated channel so failures don't disappear behind a generic UI toast.How can I help you explore Laravel packages today?