Service Container


1. Introduction

Laravel service container is a powerful tool for managing class dependencies and performing dependency injection. Dependency injection sounds fancy. Its essence is to inject class dependencies into classes through constructors or, in some cases, setter methods.

Let's look at a simple example:

 <? php namespace App\Http\Controllers; use App\User; use App\Repositories\UserRepository; use App\Http\Controllers\Controller; class UserController extends Controller { /** * The user repository implementation. * * @var UserRepository */ protected $users; /** * Create a new controller instance. *  * @param   UserRepository  $users * @return void */ public function __construct(UserRepository $users) { $this->users = $users; } /** * Show the profile for the given user. * * @param  int  $id * @return Response */ public function show($id) { $user = $this->users->find($id); return view('user.profile', ['user' => $user]); } }

In this example, UserController We need to obtain users from the data source, so we injected a service that can obtain users UserRepository , its role is similar to that of Eloquent Get user information from the database. injection UserRepository After that, we can encapsulate other implementations based on it, or simulate or create a fake UserRepository Implementation for testing.

An in-depth understanding of the Laravel service container is crucial for building powerful large-scale Laravel applications, and it is also helpful for contributing code to the Laravel core.

2. Binding

Binding foundation

Almost all service container bindings are in the Service Provider Completed in. Therefore, the containers used in the demonstration examples in this chapter are bound in the service provider.

Note: If a class is not based on any interface, it is unnecessary to bind it to the container. The container does not need to be told how to build objects, because it will automatically parse out specific objects using PHP's reflection service.

Simple binding

In a service provider, you can use the $this->app Variable to access the container, and then use the bind Method registers a binding. This method requires two parameters. The first parameter is the name of the class or interface we want to register, and the second parameter is the closure that returns the instance of the class:

 $this->app->bind('HelpSpot\API', function ($app) { return new HelpSpot\API($app->make('HttpClient')); });

Notice that we take the container itself as a parameter of the parser, and then we can use the container to resolve the child dependencies of the object we are building.

Bind a singleton

singleton The method binds a class or interface that only needs to be resolved once to the container, and then the next call to the container will return the same instance:

 $this->app->singleton('HelpSpot\API', function ($app) { return new HelpSpot\API($app->make('HttpClient')); });

Binding Instance

You can also use instance The method binds an existing object instance to the container, and then calls the container to always return the given instance:

 $api = new HelpSpot\API(new HttpClient); $this->app->instance('HelpSpot\Api', $api);

Bind original value

You may have a class that receives the injected class and needs to inject a native value such as an integer. You can easily inject any value required by this class in combination with the context:

 $this->app->when('App\Http\Controllers\UserController') ->needs('$variableName') ->give($value);

Bind interface to implementation

A very powerful function of the service container is its binding interface to the implementation. Let's suppose there is a EventPusher Interface and its implementation class RedisEventPusher , the interface's RedisEventPusher After implementation, you can register it to the service container:

 $this->app->bind( 'App\Contracts\EventPusher',  'App\Services\RedisEventPusher' );

This code tells the container that when a class needs EventPusher Will be injected RedisEventPusher , now we can do it in the constructor or any other place where we inject dependencies through the service container EventPusher Dependency injection of interfaces:

 use App\Contracts\EventPusher; /** *Create a new class instance * * @param   EventPusher  $pusher * @return void */ public function __construct(EventPusher $pusher){ $this->pusher = $pusher; }

Context binding

Sometimes we may have two classes using the same interface, but we want to inject different implementations into each class, for example, two controller dependencies Illuminate\Contracts\Filesystem\Filesystem contract Different implementations of. Larave defines a simple and smooth interface for this purpose:

 use Illuminate\Support\Facades\Storage; use App\Http\Controllers\VideoController; use App\Http\Controllers\PhotoControllers; use Illuminate\Contracts\Filesystem\Filesystem; $this->app->when(PhotoController::class) ->needs(Filesystem::class) ->give(function () { return Storage::disk('local'); }); $this->app->when(VideoController::class) ->needs(Filesystem::class) ->give(function () { return Storage::disk('s3'); });

label

In a few cases, we need to resolve all bindings under a specific category. For example, you are building one to receive multiple different Report The report aggregator implemented by the interface is registered after Report After implementation, you can use the tag Method assigns them a label:

 $this->app->bind('SpeedReport', function () { // }); $this->app->bind('MemoryReport', function () { // }); $this->app->tag(['SpeedReport', 'MemoryReport'], 'reports');

After these services are labeled, you can use the tagged Method to easily parse them:

 $this->app->bind('ReportAggregator', function ($app) { return new ReportAggregator($app->tagged('reports')); });

3. Parse

Make method

There are many ways to parse objects from containers. First, you can use make Method, which receives the class name or interface name you want to resolve as a parameter:

 $fooBar = $this->app->make('HelpSpot\API');

If your code location cannot be accessed $app Variables, you can use auxiliary functions resolve

 $api = resolve('HelpSpot\API');

Automatic injection

Finally, and most commonly used, you can simply parse objects from the container by type prompting dependencies in the constructor of the class, controller Event Listener Queue Task middleware This is the way to wait. In practice, this is the way most objects are resolved from containers.

The container will automatically inject dependency for its parsing class. For example, you can give type prompt for the application defined warehouse in the constructor of the controller, and the warehouse will automatically parse and inject this class:

 <? php namespace App\Http\Controllers; use App\Users\Repository as UserRepository; class UserController extends Controller{ /** *User warehouse instance */ protected $users; /** *Create a controller instance * * @param   UserRepository  $users * @return void */ public function __construct(UserRepository $users) { $this->users = $users; } /** *Display users by specifying IDs * * @param  int  $id * @return Response */ public function show($id) { // } }

4. Container Events

The service container will trigger an event every time it parses an object. You can use the resolving Method listens to this event:

 $this->app->resolving(function ($object, $app) { // Called when container resolves object of any type... }); $this->app->resolving(HelpSpot\API::class, function ($api, $app) { // Called when container resolves objects of type "HelpSpot\API"... });

As you can see, the parsed object will be passed to the callback function, allowing you to set additional properties for the object before it is passed to the consumer.


give the thumbs-up Cancel Like Collection Cancel Collection

<<Previous: The life cycle of a request

>>Next: Service Provider