Service Container

Introducción

The Laravel service container is a powerful tool for managing class dependencies and performing dependency injection. Dependency injection is a fancy phrase that essentially means this: class dependencies are "injected" into the class via the constructor or, in some cases, "setter" methods.

Veamos un ejemplo sencillo:

<?php

namespace App\Jobs;

use App\User;
use Illuminate\Contracts\Mail\Mailer;
use Illuminate\Contracts\Bus\SelfHandling;

class PurchasePodcast implements SelfHandling
{
    /**
     * The mailer implementation.
     */
    protected $mailer;

    /**
     * Crea una nueva instancia.
     *
     * @param  Mailer  $mailer
     * @return void
     */
    public function __construct(Mailer $mailer)
    {
        $this->mailer = $mailer;
    }

    /**
     * Comprar un podcast.
     *
     * @return void
     */
    public function handle()
    {
        //
    }
}

In this example, the PurchasePodcast job needs to send e-mails when a podcast is purchased. Así, inyectaremos un servicio que es capaz de enviar e-mails. Puesto que el servicio se inyecta, podemos intercambiarlo fácilmente con otra aplicación. Además podremos hacer un "mock", o crear una implementación simulada del mailer cuando hagamos el testing de nuestra aplicación.

Una comprensión profunda del service container de Laravel es esencial para construir una aplicación de gran alcance, así como para contribuir en el mismo core de Laravel.

Binding (enlazar)

Almost all of your service container bindings will be registered within service providers, so all of these examples will demonstrate using the container in that context. However, there is no need to bind classes into the container if they do not depend on any interfaces. The container does not need to be instructed how to build these objects, since it can automatically resolve such "concrete" objects using PHP's reflection services.

Dentro de un service provider, tendrás siempre acceso al container a través de la propiedad $this->app. 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:

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

Notice that we receive the container itself as an argument to the resolver. We can then use the container to resolve sub-dependencies of the object we are building.

Binding A Singleton

The singleton method binds a class or interface into the container that should only be resolved one time, and then that same instance will be returned on subsequent calls into the container:

$this->app->singleton('FooBar', function ($app) {
    return new FooBar($app['SomethingElse']);
});

Binding Instances

Podrías enlazar una instancia de un objeto existente al container utilizando el método instance. Se retornará esa misma instancia en llamadas posteriores al container:

$fooBar = new FooBar(new SomethingElse);

$this->app->instance('FooBar', $fooBar);

Enlazando (binding) interfaces a implementaciones

Una de las características del service container es su capacidad para enlazar una interfaz a una implementación determinada. For example, let's assume we have an EventPusher interface and a RedisEventPusher implementation. Once we have coded our RedisEventPusher implementation of this interface, we can register it with the service container like so:

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

This 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 a constructor, or any other location where dependencies are injected by the service container:

use App\Contracts\EventPusher;

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

Binding contextual

En ocasiones puedes tener dos clases que utilizan la misma interfaz, pero quieres inyectar implementaciones diferentes a cada clase. Por ejemplo, cuando nuestro sistema recibe un nuevo Pedido, podemos querer lanzar un evento vía PubNub en lugar de Pusher. Laravel provee una simple y fluida interfaz para definir este comportamiento:

$this->app->when('App\Handlers\Commands\CreateOrderHandler')
          ->needs('App\Contracts\EventPusher')
          ->give('App\Services\PubNubEventPusher');

You may even pass a Closure to the give method:

$this->app->when('App\Handlers\Commands\CreateOrderHandler')
          ->needs('App\Contracts\EventPusher')
          ->give(function () {
                // Resolve dependency...
            });

Etiquetado

En ocasiones, puedes necesitar resolver todos los enlaces de una "categoría". Por ejemplo, un gestor de reportes que reciba un array de varias implementaciones diferentes de la interfaz Report. Después de registrar las implementaciones Report, puedes asignarlas utilizando el método tag:

$this->app->bind('SpeedReport', function () {
    //
});

$this->app->bind('MemoryReport', function () {
    //
});

$this->app->tag(['SpeedReport', 'MemoryReport'], 'reports');

Una vez que los servicios han sido etiquetados, puedes resolverlos utilizando el método tagged:

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

Resolver

There are several ways to resolve something out of the container. First, you may use the make method, which accepts the name of the class or interface you wish to resolve:

$fooBar = $this->app->make('FooBar');

Secondly, you may access the container like an array, since it implements PHP's ArrayAccess interface:

$fooBar = $this->app['FooBar'];

Lastly, but most importantly, you may simply "type-hint" the dependency in the constructor of a class that is resolved by the container, including controllers, event listeners, queue jobs, middleware, and more. In practice, this is how most of your objects are resolved by the container.

The container will automatically inject dependencies for the classes it resolves. For example, you may type-hint a repository defined by your application in a controller's constructor. The repository will automatically be resolved and injected into the class:

<?php

namespace App\Http\Controllers;

use Illuminate\Routing\Controller;
use App\Users\Repository as UserRepository;

class UserController extends Controller
{
    /**
     * The user repository instance.
     */
    protected $users;

    /**
     * Crear una nueva instancia del controlador.
     *
     * @param  UserRepository  $users
     * @return void
     */
    public function __construct(UserRepository $users)
    {
        $this->users = $users;
    }

    /**
     * Mostrar el usuario con el ID dado.
     *
     * @param  int  $id
     * @return Response
     */
    public function show($id)
    {
        //
    }
}

Eventos del container

The service container fires an event each time it resolves an object. You may listen to this event using the resolving method:

$this->app->resolving(function ($object, $app) {
    // Called when container resolves object of any type...
});

$this->app->resolving(function (FooBar $fooBar, $app) {
    // Called when container resolves objects of type "FooBar"...
});

As you can see, the object being resolved will be passed to the callback, allowing you to set any additional properties on the object before it is given to its consumer.