orchestra/support-providers
Orchestra Support Providers delivers service-provider bindings and bootstrap helpers for Orchestra Platform, simplifying registration of core support services and keeping Laravel package integration consistent across applications.
orchestra/support-providers is provider plumbing: it ships abstract ServiceProvider base classes and convention-driven helpers that Orchestra Platform packages and modules extend. You will touch it in two situations — authoring a provider for an Orchestra module, or debugging a provider that arrived transitively via Orchestra.
Install (only when needed):
composer require orchestra/support-providers
composer show orchestra/support-providers --all
The second command is not optional trivia — read the require block (Laravel and PHP constraints) before you commit. This package's tag line tracks Laravel majors, so composer update is the first thing that will block a framework upgrade if the version is stale.
Where to look first, in this order:
src/ — the package is small; read it end to end. The abstract classes are the entire API surface you consume. There is effectively no documentation, so the source is the spec.tests/ — the de facto usage examples. Anything ambiguous in src/ is disambiguated by what the tests assert about registration and deferred loading.composer.lock — confirm whether it is already present and at what version, and whether it drags in a wider Orchestra graph or sits leaf-level.composer.json → extra.laravel.providers — decide whether the package auto-discovers a provider; if you also register it manually you will get double registration.First use case — a module provider. Extend one of the provided abstract providers instead of Illuminate\Support\ServiceProvider, implement register() for container bindings and boot() for anything that needs other services resolved, and keep your module's provider tiny. The point of the base class is that command registration, config merging, and deferred-service declaration follow Orchestra conventions rather than your own invention — so your first provider should look boring, not clever.
Realistic time box: half a day to read the source, validate constraints, and get one module resolving from the container.
Register bindings in register(), resolve in boot(). Standard Laravel discipline, but it matters more here because Orchestra's deferred-provider semantics are the reason the package exists. Anything you put in register() must be safe to run without the rest of the app being booted.
Deferred services via the deferrable contract. If a binding is expensive and not needed on every request, declare it deferred through the base class rather than registering eagerly. The package standardizes this so package authors don't hand-roll the provides() whitelist. Verify the exact contract name against src/ for your pinned version — Laravel has changed deferred-loading semantics across majors, and the package's wrapper is a moving target.
class MyModuleServiceProvider extends AbstractServiceProvider
{
public function register()
{
$this->app->singleton(MyService::class, fn ($app) => new MyService(
$app['config']->get('mymodule'),
));
}
public function boot()
{
$this->mergeConfigFrom(__DIR__.'/../config/mymodule.php', 'mymodule');
$this->publishes([
__DIR__.'/../config/mymodule.php' => config_path('mymodule.php'),
], 'mymodule-config');
}
}
Command registration through the provider, not Kernel.php. Orchestra's base classes expose a convention for collecting console commands. Register module commands inside the module's provider so uninstalling the module removes its commands. This is the single biggest boilerplate reduction the package actually delivers.
Config merge then publish, in that order. mergeConfigFrom gives sane defaults from the package; publishes lets the app override. Never publish as a side effect of boot() — it belongs behind vendor:publish --tag=....
One provider per module, thin. Resist building a hierarchy of providers. Every class extending the Orchestra base is a class you must migrate if you ever leave the ecosystem; keep the count small and visible so the exit cost stays legible.
Integration tip — wrap it. Define an internal base class extending Illuminate\Support\ServiceProvider that delegates to the Orchestra abstract only where interop is genuinely required:
abstract class ModuleProvider extends Illuminate\Support\ServiceProvider
{
// Orchestra interop confined to this one file
}
Your module providers extend your base class. If the dependency rots, you change one file.
Testing pattern. Assert that services actually resolve — $this->assertInstanceOf(MyService::class, $this->app->make(MyService::class)) — rather than asserting the provider was registered. Registration succeeding and the binding being resolvable are different facts, and only the second one is what production needs.
Silent boot failure is the signature bug. A provider that throws or mis-registers surfaces later as a null binding or a "target not instantiable" deep in a request, far from the cause. When a service is mysteriously missing, dump app()->getLoadedProviders() and app()->getDeferredServices() before you touch anything else — the answer is usually "the provider never loaded" or "it's deferred and the deferred class list doesn't include what you bound."
Deferred-provider mismatch. Orchestra wraps Laravel's deferred loading, and the wrapper can drift from the framework's semantics across majors. A service that resolves cleanly in a feature test may throw in a queued job because the deferred service wasn't registered on that worker's lifecycle. If a service resolves in HTTP but not in a job or an Octane worker, look here first.
Version deadlock is the real operational risk. The package's Laravel constraint can gate a framework upgrade for your entire application. Before adopting, run a dry-run composer update against the Laravel major you actually run. Never resolve a conflict with --ignore-platform-reqs; you'll get a provider lifecycle mismatch that only shows up in production.
Convention drift with vendor:publish. Orchestra's config-merge and manifest conventions can conflict with standard publish flows, producing confusing precedence — the app has a config value, the package has a default, and which one wins depends on merge order relative to your publish. When a config value "won't take," check the published file's merge order before you check the value itself.
Auto-discovery double-registration. If the package declares itself in extra.laravel.providers and you also list it in bootstrap/providers.php, it boots twice. Usually harmless for bindings, occasionally fatal for anything that appends (Event::listen, middleware registration, route registration).
Bus factor of one. The community surface is effectively zero — no Stack Overflow corpus, no downstream bug-fixing, no vendor SLA. Your escalation path is reading src/ and patching. Budget for that explicitly, keep the wrapper described above, and pre-agree a fork trigger (e.g. "no release within N weeks of a Laravel major").
Extension points to know. The abstract providers themselves, config merge behavior, and the command-collection convention. That's it. There is no plugin sandbox, no versioned contract, no capability permission layer — the package handles registration, not governance. Don't expect it to solve plugin isolation.
Liveness check as a recurring task. Last commit date, open issues, current Laravel-major support, archived status on Packagist. Put it on a calendar with a named owner, or the next framework upgrade becomes someone's surprise incident.
Don't adopt it for a vanilla Laravel app. bootstrap/providers.php, package auto-discovery, and native deferred providers already cover the use case with none of the bus-factor risk. This package earns its keep only inside Orchestra Platform or when authoring third-party modules that must target Orchestra's conventions.
How can I help you explore Laravel packages today?