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

Discovery Laravel Package

thecodingmachine/discovery

Discover PHP classes implementing a given interface by scanning your Composer classmap at runtime. Thecodingmachine/Discovery makes building plugin systems and auto-registering services simple, with no manual configuration or boilerplate required.

View on GitHub
Deep Wiki
Context7

Getting Started

Install it at the root of the project (it works across all Composer packages, so it belongs in the app, not a library):

composer require thecodingmachine/discovery

Composer 2.2+ will prompt you to allow the plugin — accept it (config.allow-plugins), otherwise nothing is generated.

The mental model has two halves. Publishers (any installed package) declare assets in their own composer.json:

"extra": {
    "discovery": {
        "my-app-assets": [
            { "type": "file", "value": "dist/widget.js" },
            { "type": "file", "value": "dist/widget.css" }
        ]
    }
}

Consumers read those assets back through a single API:

use TheCodingMachine\Discovery\Discovery;

foreach (Discovery::getInstance()->get('my-app-assets') as $asset) {
    echo $asset->getValue(); // "dist/widget.js"
}

First real use case in a Laravel app: let a Composer-installed package ship its own JS/CSS, then copy them into public/vendor/<package>/ in a post-autoload-dump hook or a small Artisan command — no manual copying, no hard-coded list of vendor packages in your app.

Implementation Patterns

Type-as-tag registry. The string key in extra.discovery is arbitrary. Use it as a namespace: laravel.route-file, laravel.migration-path, admin-menu-item, vite.entry. This is the package's real value — a decentralized plugin registry read from Composer metadata rather than a central config array.

Publishing to public/. The "publish" workflow copies declared files into a web-accessible directory, tracking a manifest so unchanged files aren't recopied:

Discovery::publishAssets(public_path('vendor'), force: false);

Wire this into your deploy script (after composer install --no-dev) or a php artisan discovery:publish command. In dev, pass force: true or delete the manifest when package versions change.

Priority-based overrides. Each asset entry can carry a priority. Higher-priority entries should win when you want the app to override a vendor-provided asset — useful for the laravel.* type where your app config should shadow a package default. Sort/iterate and take the first match, or merge all with the app first.

Non-file assets. Assets can be strings (a class name, a route file, a config fragment). A publisher can register a service provider class as a string asset, and your app discovers all of them and calls register(). This is a poor-man's package discovery mechanism — the same idea Laravel uses natively now, but driven by Composer.

Laravel integration tip. Wrap reads behind a cached service provider. The discovery file lives under vendor/ and is regenerated on composer dump-autoload; resolve it once in boot() and bind results into the container so you're not re-parsing on every request.

Gotchas and Tips

  • Scripts don't run from dependencies. Composer only executes scripts from the root composer.json. Any package documenting a post-autoload-dump hook will not fire for its consumers. Keep the hook in your app's composer.json (or rely on the plugin), and re-run composer dump-autoload after changing any extra.discovery block.
  • Stale discovery file. If get() returns an empty array, the generated file is almost certainly out of date. Regenerate it, and inspect it directly — it's the ground truth of what was published.
  • Paths are relative to the package root, not your project root. When resolving a FileAsset, prepend the package's install path (available via Composer's installed.json, or the asset's own location metadata). Don't assume base_path().
  • Publishing is a copy, not a symlink. Changes to vendor files won't reflect until you republish. The manifest/force behavior is your friend in CI, your enemy in local dev — delete the manifest when in doubt.
  • Missing allow-plugins silently breaks generation on Composer 2.2+. Check composer config allow-plugins first when nothing works.
  • Malformed JSON is silent-ish. A syntax error or wrong shape in a vendor composer.json extra block generally won't throw at read time — it just drops entries. Validate entries (type + value keys) in your own code if you depend on them.
  • Performance. It scans installed packages' composer.json files. Fine at deploy, wasteful per-request — cache the resolved list.
  • Maintenance risk. Last release 2022-09-09 and low activity. It's a thin, stable layer over Composer metadata, so it will keep working, but don't expect new features or PHP-version fixes. For a new Laravel project, consider whether Laravel's native extra.laravel service-provider discovery or a simple composer.json merge in your own service provider covers the same ground; use this package when you specifically need vendored package assets copied to public/.
  • Extension point. If you need custom asset behavior, subclass the base asset type and register the type — the discovery layer is designed to be extended rather than forked.
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.
skyraptor/chatter
edlugz/sasapay
abbasudo/artisan-route
mirvan/ippanel
ignibyte/aic-framework
glavweb/data-schema-bundle
glavweb/datagrid-bundle
tacticmedia/scim-server
uni-method/frame
neika-ai/sdk
adachsoft/php-code-style
pharaonic/laravel-notification-dependencies
beautinow/translug
dev-brainiac/bot-cloaked
octoberrain/html
octoberrain/extension
octoberrain/exception
octoberrain/contracts
adachsoft/api-version-bundle
cgv/localized-resource