catchadmin/plugin-installer
Installer for CatchAdmin plugins that simplifies fetching, installing, and registering plugin packages in your Laravel admin application. It handles routine setup steps so you can add new modules and features without manual configuration or file copying.
catchadmin/plugin-installer is a Composer plugin that belongs to the CatchAdmin ecosystem: it intercepts package installation so that packages tagged as CatchAdmin plugins land in the application's plugin/module directory (typically plugins/ or modules/) instead of vendor/, and it hooks the Composer lifecycle to register them with the host app. Treat it as infrastructure, not a library — you rarely call it, you configure it.
Minimal steps
composer require catchadmin/plugin-installer
{
"config": {
"allow-plugins": {
"catchadmin/plugin-installer": true
}
}
}
composer install -vvv 2>&1 | grep -i plugin
composer show catchadmin/plugin-installer
composer.json (type, extra, require of composer-plugin-api) to see which lifecycle events it subscribes to;src/ for the PluginInterface / EventSubscriberInterface implementation — that file is the documentation;composer.json extra block, which is where any installer paths or plugin-type mappings are configured;plugins/ directory after a test install.First use case: install a single plugin package you know is built for CatchAdmin and watch it appear under plugins/ rather than vendor/, then enable it in the admin UI (or via the framework's plugin artisan command) and confirm its routes/migrations register. That one round-trip tells you more about the package than any README.
Do this on a throwaway branch first. With zero stars and zero dependents there is no community safety net, and the package's real behaviour is defined by whatever version Composer resolves for you.
Declaring a package as a plugin. The installer only acts on packages whose composer.json carries the expected type (something like catch-plugin / catchadmin-plugin — verify the exact string in the installer source before publishing your own). A plugin's composer.json therefore looks like:
{
"name": "acme/catch-blog",
"type": "catch-plugin",
"require": { "php": "^8.1" },
"extra": { "catch": { "name": "Blog", "version": "1.0.0" } }
}
Without the correct type, the package installs into vendor/ like any other and the installer silently ignores it — the single most common source of "the plugin isn't showing up".
Local development with path repositories. For in-house plugins, symlink them so edits are live:
{
"repositories": [
{ "type": "path", "url": "../catch-blog", "options": { "symlink": true } }
],
"require": { "acme/catch-blog": "*" }
}
This is the workflow you'll actually live in day to day — edit in the plugin repo, see changes in the host app without re-running composer update.
Private distribution. CatchAdmin plugins are rarely on Packagist. Pair the installer with a vcs repository, a Satis/Private Packagist index, or a CI job that publishes tagged zips. Tag releases; dev-main installs combined with a plugin that mutates the filesystem are hard to roll back.
Deployment. Decide explicitly whether plugins/ is a build artifact or a committed directory. The reliable pattern is: composer install --no-dev --optimize-autoloader during deploy, let the installer place plugins, then run migrations and asset publishing as separate steps. Don't mix plugin installation into a post-deploy hook that also clears caches — you want the failure to be visible.
Per-plugin lifecycle work. Migrations, config publishing and asset publishing belong to the plugin's own service provider, not to the installer. Use the installer only for placement and discovery; use standard Laravel publishes() / loadMigrationsFrom() for everything else, otherwise you couple your plugin to installer internals.
allow-plugins is the number-one failure. If nothing happens, check composer config allow-plugins first. Related: plugins are not loaded for composer dump-autoload when COMPOSER points at a different manifest, and some CI images ship a global Composer config that blocks plugins project-wide.vendor/, its PSR-4 mapping in vendor/composer/autoload_psr4.php may no longer resolve. If you get Class not found for a freshly installed plugin, run composer dump-autoload and inspect the generated autoload files; if the mapping is still vendor/-relative, the installer expects the plugin to declare its own autoloader or to be registered via the app's plugin loader instead of Composer's.composer remove is usually not symmetric. Installers commonly handle install/uninstall of the files but leave directories, published config and DB migrations behind. Clean up deliberately; write down what a plugin installed.post-autoload-dump re-runs on every dump (good for discovery, bad if it copies files and overwrites local edits). Work on post-install-cmd won't re-run on composer update. Read which events the plugin subscribes to before blaming it.symlink: true behave differently on Windows and inside Docker bind mounts. If a plugin works locally and not in the container, this is why.composer install -vvv plus COMPOSER_DEBUG_EVENTS=1 will show you which events fire and in what order. That's the fastest path to understanding an under-documented installer.extra.installer-paths-style configuration (if it delegates to composer/installers) or a package-specific extra.catch / extra.plugin block. Those are your supported knobs; anything else means editing the installer source — at which point, fork it.composer.lock, pin to an exact version, and keep a fork in-house if you depend on it in production. Read the diff of the installer itself on upgrade — it runs with full filesystem privileges at install time, which is a meaningful trust boundary for a package this young.How can I help you explore Laravel packages today?