Laravel Service Container overview
In a nutshell
The Laravel service container is responsible for managing your classes’s dependencies and performing dependency injection via the constructor.
Most of the time, type hinting your dependencies in your constructors like in the above example will suffice. But you might need to manually interact with the container in the following situations:
- If you write a class that implements an interface and you wish to type-hint that interface on a route or class constructor, you must tell the container how to resolve that interface.
- If you are writing a Laravel package that you plan to share with other Laravel developers, you may need to bind your package’s services into the container.
Recipes
Bind a class instance to the service container
You typically bind a class instance within service providers.
Within a service provider, you always have access to the container via the $this->app
property. We can register a binding using the bind
method, passing the class or interface name that we wish to register along with a closure that returns an instance of the class:
use App\Services\Transistor;
use App\Services\PodcastParser;
use Illuminate\Contracts\Foundation\Application;
$this->app->bind(Transistor::class, function (Application $app) {
return new Transistor($app->make(PodcastParser::class));
});
You can also bind to the container outside a service provider via the App
facade:
use App\Services\Transistor;
use Illuminate\Contracts\Foundation\Application;
use Illuminate\Support\Facades\App;
App::bind(Transistor::class, function (Application $app) {
// ...
});
You can use the bindIf
method to register a container binding only if a binding has not already been registered for the given type:
$this->app->bindIf(Transistor::class, function (Application $app) {
return new Transistor($app->make(PodcastParser::class));
});
Bind a singleton
The singleton
method binds a class or interface into the container that should only be resolved one time. Once a singleton binding is resolved, the same object instance will be returned on subsequent calls into the container:
use App\Services\Transistor;
use App\Services\PodcastParser;
use Illuminate\Contracts\Foundation\Application;
$this->app->singleton(Transistor::class, function (Application $app) {
return new Transistor($app->make(PodcastParser::class));
});
You may use the singletonIf
method to register a singleton container binding only if a binding has not already been registered for the given type:
$this->app->singletonIf(Transistor::class, function (Application $app) {
return new Transistor($app->make(PodcastParser::class));
});
Bind a scoped singleton
The scoped
method binds a class or interface into the container that should only be resolved one time within a given Laravel request / job lifecycle. While this method is similar to the singleton
method, instances registered using the scoped
method will be flushed whenever the Laravel application starts a new “lifecycle”, such as when a Laravel Octane worker processes a new request or when a Laravel queue worker processes a new job:
use App\Services\Transistor;
use App\Services\PodcastParser;
use Illuminate\Contracts\Foundation\Application;
$this->app->scoped(Transistor::class, function (Application $app) {
return new Transistor($app->make(PodcastParser::class));
});
Bind an interface to an implementation
use App\Contracts\EventPusher;
use App\Services\RedisEventPusher;
$this->app->bind(EventPusher::class, RedisEventPusher::class);
This statement tells the container that it should inject the RedisEventPusher
when a class needs an implementation of EventPusher
. Now we can type-hint the EventPusher
interface in the constructor of a class that is resolved by the container.
Contextual binding
This statement tells the container that it should inject the RedisEventPusher
when a class needs an implementation of EventPusher
. Now we can type-hint the EventPusher
interface in the constructor of a class that is resolved by the container.
use App\Http\Controllers\PhotoController;
use App\Http\Controllers\UploadController;
use App\Http\Controllers\VideoController;
use Illuminate\Contracts\Filesystem\Filesystem;
use Illuminate\Support\Facades\Storage;
$this->app->when(PhotoController::class)
->needs(Filesystem::class)
->give(function () {
return Storage::disk('local');
});
$this->app->when([VideoController::class, UploadController::class])
->needs(Filesystem::class)
->give(function () {
return Storage::disk('s3');
});
Bind primitives
use App\Http\Controllers\UserController;
$this->app->when(UserController::class)
->needs('$variableName')
->give($value);
Extend bindings
$this->app->extend(Service::class, function (Service $service, Application $app) {
return new DecoratedService($service);
});
Resolving
Resolve a class instance from the container
use App\Services\Transistor;
// 1. Via $this->app
$transistor = $this->app->make(Transistor::class);
// 2. Via the App facade
$transistor = App::make(Transistor::class);
// 3. Via the app helper
$transistor = app(Transistor::class);
// 4. Dependency injection
use Illuminate\Container\Container;
/**
* Create a new class instance.
*/
public function __construct(
protected Container $container
) {}
If some of your class’s dependencies are not resolvable via the container, you may inject them by passing them as an associative array into the makeWith
method. For example, we may manually pass the $id
constructor argument required by the Transistor
service:
use App\Services\Transistor;
$transistor = $this->app->makeWith(Transistor::class, ['id' => 1]);
Check if a class or interface has already been bound
if ($this->app->bound(Transistor::class)) {
// ...
}
Invoke a method and inject dependencies
Given this class:
<?php
namespace App;
use App\Services\AppleMusic;
class PodcastStats
{
/**
* Generate a new podcast stats report.
*/
public function generate(AppleMusic $apple): array
{
return [
// ...
];
}
}
You can execute the generate
method via the container like so:
use App\PodcastStats;
use Illuminate\Support\Facades\App;
$stats = App::call([new PodcastStats, 'generate']);
Or even do something like this to run a closure via the container:
use App\Services\AppleMusic;
use Illuminate\Support\Facades\App;
$result = App::call(function (AppleMusic $apple) {
// ...
});
Container events
use App\Services\Transistor;
use Illuminate\Contracts\Foundation\Application;
$this->app->resolving(Transistor::class, function (Transistor $transistor, Application $app) {
// Called when container resolves objects of type "Transistor"...
});
$this->app->resolving(function (mixed $object, Application $app) {
// Called when container resolves object of any type...
});