Contratos (Contracts)

Introducción

Los contratos de Laravel son un conjunto de interfaces que definen los principales servicios proporcionados por el framework. For example, a Illuminate\Contracts\Queue\Queue contract defines the methods needed for queueing jobs, while the Illuminate\Contracts\Mail\Mailer contract defines the methods needed for sending e-mail.

Cada contrato tiene una implementación correspondiente proporcionada por el framework. For example, Laravel provides a queue implementation with a variety of drivers, and a mailer implementation that is powered by SwiftMailer.

Todos los contratos de Laravel se encuentran en su propio repositorio de GitHub. This provides a quick reference point for all available contracts, as well as a single, decoupled package that may be utilized by package developers.

Contracts Vs. Facades

Laravel's facades provide a simple way of utilizing Laravel's services without needing to type-hint and resolve contracts out of the service container. However, using contracts allows you to define explicit dependencies for your classes. For most applications, using a facade is just fine. However, if you really need the extra loose coupling that contracts can provide, keep reading!

¿Por qué contratos?

You may have several questions regarding contracts. Why use interfaces at all? Isn't using interfaces more complicated? Let's distil the reasons for using interfaces to the following headings: loose coupling and simplicity.

Acoplamiento flexible

En primer lugar, vamos a repasar algo de código que se acopla firmemente a una implementación de caché. Considera lo siguiente:

<?php

namespace App\Orders;

class Repository
{
    /**
     * The cache.
     */
    protected $cache;

    /**
     * Create a new repository instance.
     *
     * @param  \SomePackage\Cache\Memcached  $cache
     * @return void
     */
    public function __construct(\SomePackage\Cache\Memcached $cache)
    {
        $this->cache = $cache;
    }

    /**
     * Retrieve an Order by ID.
     *
     * @param  int  $id
     * @return Order
     */
    public function find($id)
    {
        if ($this->cache->has($id)) {
            //
        }
    }
}

En esta clase, el código está fuertemente acoplado a una implementación de caché determinada. Está fuertemente acoplado porque estamos dependiendo de una clase Cache de un package concreto. Si el API de este paquete cambia, nuestro código cambiaría también.

Asímismo, si queremos reemplazar la tecnología caché (Memcache) por alguna otra (Redis), tendremos que modificar nuestro código de nuevo. Nuestro repositorio no debería tener tanto conocimiento acerca de quién está proporcionando los datos o cómo se ofrecen.

En lugar de este enfoque, podemos mejorar nuestro código dependiendo de una interfaz agnóstica:

<?php

namespace App\Orders;

use Illuminate\Contracts\Cache\Repository as Cache;

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

Ahora nuestro código no está acoplado a ningún proveedor específico, o incluso Laravel. Puesto que el paquete de contratos no contiene ninguna implementación ni dependencias, puedes programar implementaciones alternativas de cualquier contrato de forma sencilla, permitiéndote reemplazar tu implementación de caché sin modificar ningún elemento que haga uso de ella.

Simplicidad

Cuando todos los servicios de Laravel están definidos dentro de interfaces simples, es muy fácil determinar la funcionalidad proporcionada por un servicio concreto. Los contratos son como una documentación breve de las características del framework.

Además, cuando dependes de interfaces simples, el código resulta mucho más sencillo de entender y mantener. En lugar de rastrear qué métodos están disponibles en una clase compleja, puedes hacer referencia a una limpia y simple interfaz.

Referencia de contratos

Esto es una referencia a los Contratos de Laravel más importantes y a su "facade" homóloga:

Contrato References Facade
Illuminate\Contracts\Auth\Guard Auth
Illuminate\Contracts\Auth\PasswordBroker Password
Illuminate\Contracts\Bus\Dispatcher Bus
Illuminate\Contracts\Broadcasting\Broadcaster  
Illuminate\Contracts\Cache\Repository Caché
Illuminate\Contracts\Cache\Factory Cache::driver()
Illuminate\Contracts\Config\Repository Config
Illuminate\Contracts\Container\Container App
Illuminate\Contracts\Cookie\Factory Cookie
Illuminate\Contracts\Cookie\QueueingFactory Cookie::queue()
Illuminate\Contracts\Encryption\Encrypter Crypt
Illuminate\Contracts\Events\Dispatcher Event
Illuminate\Contracts\Filesystem\Cloud  
Illuminate\Contracts\Filesystem\Factory File
Illuminate\Contracts\Filesystem\Filesystem File
Illuminate\Contracts\Foundation\Application App
Illuminate\Contracts\Hashing\Hasher Hash
Illuminate\Contracts\Logging\Log Log
Illuminate\Contracts\Mail\MailQueue Mail::queue()
Illuminate\Contracts\Mail\Mailer Correo
Illuminate\Contracts\Queue\Factory Queue::driver()
Illuminate\Contracts\Queue\Queue Queue
Illuminate\Contracts\Redis\Database Redis
Illuminate\Contracts\Routing\Registrar Route
Illuminate\Contracts\Routing\ResponseFactory Response
Illuminate\Contracts\Routing\UrlGenerator URL
Illuminate\Contracts\Support\Arrayable  
Illuminate\Contracts\Support\Jsonable  
Illuminate\Contracts\Support\Renderable  
Illuminate\Contracts\Validation\Factory Validator::make()
Illuminate\Contracts\Validation\Validator  
Illuminate\Contracts\View\Factory View::make()
Illuminate\Contracts\View\View  

Cómo usar los contratos

So, how do you get an implementation of a contract? It's actually quite simple.

Many types of classes in Laravel are resolved through the service container, including controllers, event listeners, middleware, queued jobs, and even route Closures. Para obtener la implementación de un contrato, simplemente "type-hint" la interfaz en el método constructor de la clase a resolver.

For example, take a look at this event listener:

<?php

namespace App\Listeners;

use App\User;
use App\Events\NewUserRegistered;
use Illuminate\Contracts\Redis\Database;

class CacheUserInformation
{
    /**
     * The Redis database implementation.
     */
    protected $redis;

    /**
     * Crea una nueva instancia del controlador de eventos.
     *
     * @param  Database  $redis
     * @return void
     */
    public function __construct(Database $redis)
    {
        $this->redis = $redis;
    }

    /**
     * Gestiona el evento.
     *
     * @param  NewUserRegistered  $event
     * @return void
     */
    public function handle(NewUserRegistered $event)
    {
        //
    }
}

Cuando se resuelve el capturador de eventos, el service container leerá los type-hints del constructor de la clase e inyectará los elementos apropiados. To learn more about registering things in the service container, check out its documentation.