Skip to content

Organizing Bindings with Providers

Once you have more than a handful of bindings, your bootstrap file gets messy. Service providers let you group related bindings into focused classes.

A provider is just an invokable class that receives the app instance:

class DatabaseProvider
{
public function __invoke(App $app): void
{
$app->singleton(DB::class, fn() => new DB($app->env('DATABASE_URL')));
$app->bind(UserRepository::class, PostgresUserRepository::class);
$app->bind(PostRepository::class, PostgresPostRepository::class);
}
}

Pass provider class names to configure():

app()
->configure(DatabaseProvider::class)
->configure(AuthProvider::class)
->configure(PaymentProvider::class);

Providers run in the order you register them.

Providers resolve through the container, so type-hint whatever you need:

class PaymentProvider
{
public function __construct(private Env $env) {}
public function __invoke(App $app): void
{
$app->singleton(PaymentGateway::class, fn() => new Stripe(
$this->env->get('STRIPE_SECRET')
));
}
}

For one-off configuration that doesn’t need a full class, pass a closure:

app()->configure(function($app) {
$app->singleton(DB::class, fn() => new DB($app->env('DATABASE_URL')));
});

Or require a file that returns a closure:

config/services.php
return function(App $app) {
$app->singleton(DB::class, fn() => new DB($app->env('DATABASE_URL')));
$app->bind(UserRepository::class, PostgresUserRepository::class);
};
index.php
app()->configure(require 'config/services.php');

Here’s how you might organize providers in a real application:

project/
├── index.php
├── src/
│ └── Providers/
│ ├── DatabaseProvider.php
│ ├── AuthProvider.php
│ └── PaymentProvider.php
└── routes/
└── api.php
index.php
require 'vendor/autoload.php';
app()
->configure(DatabaseProvider::class)
->configure(AuthProvider::class)
->configure(PaymentProvider::class);
require 'routes/api.php';
app()->run();