Introducción

Los Contratos de Laravel son un conjunto de interfaces que definen los servicios principales proporcionados por el framework. Por ejemplo, un contrato Illuminate\Contracts\Queue\Queue define los métodos necesarios para las colas de trabajo, mientras que el contrato Illuminate\Contracts\Mail\Mailer define los métodos necesarios para el envío de correos electrónicos.

Cada contrato tiene una implementación correspondiente provista por el framework. Por ejemplo, Laravel proporciona una implementación de cola con una variedad de conductores (drivers), y una implementación de envío de correo electrónico que funciona con SwiftMailer.

Todos los contratos de Laravel residen en su repositorio de GitHub propio. Esto proporciona un punto de referencia rápido para todos los contratos disponibles, así como un paquete único y desacoplado que puede ser utilizado por los desarrolladores de paquetes.

Contratos vs. facades

Las facades de Laravel y las funciones de ayuda (helpers) proporcionan una forma sencilla de utilizar los servicios de Laravel sin necesidad de determinar el tipo y resolver contratos fuera del contenedor de servicios. En la mayoría de los casos, cada facade tiene un contrato equivalente.

A diferencia de las facades, que no necesitan que las requieras en el constructor de su clase, los contratos te permiten definir dependencias explícitas para tus clases. Algunos desarrolladores prefieren definir explícitamente sus dependencias de esta manera y, por lo tanto, prefieren usar contratos, mientras que otros desarrolladores disfrutan de la conveniencia de las facades.

La mayoría de las aplicaciones funcionarán bien sin importar si prefieres facades o contratos. Sin embargo, si estás construyendo un paquete, debes considerar seriamente el uso de contratos, ya que serán más fáciles de probar en un contexto de paquete.

Cuándo usar contratos

Como se discutió en otro lugar, gran parte de la decisión de usar contratos o facades se reducirá a los gustos personales y los gustos del equipo de desarrollo. Tanto los contratos como las facades se pueden utilizar para crear aplicaciones Laravel robustas y bien probadas. Mientras mantengas enfocadas las responsabilidades de tu clase, notarás muy pocas diferencias prácticas entre el uso de contratos y facades.

Sin embargo, todavía puedes tener varias preguntas con respecto a los contratos. Por ejemplo, ¿por qué usar interfaces? ¿No es más complicado usar interfaces? Detallemos las razones para utilizar interfaces en los siguientes encabezados: bajo acoplamiento y simplicidad.

Bajo acoplamiento

Primero, revisemos algunos códigos que están estrechamente acoplado a una implementación de caché. Considera lo siguiente:

<?php

namespace App\Orders;

class Repository
{
    /**
    * The cache instance.
    */
    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á estrechamente acoplado a una implementación de caché determinada. Está estrechamente acoplado porque dependemos de una clase de caché concreta de un proveedor de paquetes. Si la API de ese paquete cambia, nuestro código también debe cambiar.

Del mismo modo, si queremos reemplazar nuestra tecnología de caché subyacente (Memcached) con otra tecnología (Redis), nuevamente tendremos que modificar nuestro repositorio. Nuestro repositorio no debe tener tanto conocimiento sobre quién les proporciona los datos o cómo los proporcionan.

En lugar de este enfoque, podemos mejorar nuestro código dependiendo de una interfaz simple e independiente del proveedor:

<?php

namespace App\Orders;

use Illuminate\Contracts\Cache\Repository as Cache;

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

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

Ahora el código no está acoplado a ningún proveedor específico, ni siquiera a Laravel. Dado que el paquete de contratos no contiene implementación ni dependencias, puedes escribir fácilmente una implementación alternativa de cualquier contrato dado, lo que te permite reemplazar tu implementación de caché sin modificar ninguno de los códigos que consumen caché.

Simplicidad

Cuando todos los servicios de Laravel están claramente definidos dentro de interfaces simples, es muy fácil determinar la funcionalidad ofrecida por un servicio dado. Los contratos sirven como documentación sucinta de las características del framework.

Además, cuando dependes de interfaces simples, tu código es más fácil de entender y mantener. En lugar de rastrear qué métodos están disponibles dentro de una clase grande y complicada, puedes hacer referencia a una interfaz sencilla y limpia.

Cómo usar contratos

Entonces, ¿Cómo se obtiene una implementación de un contrato? En realidad es bastante simple.

Muchos tipos de clases en Laravel se resuelven a través del contenedor de servicio, incluyendo controladores, los listeners de eventos, middleware, trabajos de cola e incluso las Closures de rutas. Por lo tanto, para obtener una implementación de un contrato, puedes simplemente «declarar el tipo» de la interfaz en el constructor de la clase que se está resolviendo.

Por ejemplo, veamos este listener de eventos:

<?php

namespace App\Listeners;

use App\Events\OrderWasPlaced;
use App\User;
use Illuminate\Contracts\Redis\Factory;

class CacheOrderInformation
{
    /**
    * The Redis factory implementation.
    */
    protected $redis;

    /**
    * Create a new event handler instance.
    *
    * @param  Factory  $redis
    * @return void
    */
    public function __construct(Factory $redis)
    {
        $this->redis = $redis;
    }

    /**
    * Handle the event.
    *
    * @param  OrderWasPlaced  $event
    * @return void
    */
    public function handle(OrderWasPlaced $event)
    {
        //
    }
}

Cuando se resuelve el listener de evento, el contenedor de servicios leerá las declaraciones de tipo en el constructor de la clase e inyectará el valor apropiado. Para obtener más información sobre cómo registrar cosas en el contenedor de servicios, consulte su documentación.

Referencia de contratos

Esta tabla proporciona una referencia rápida a todos los contratos de Laravel y sus facades equivalentes:

Contrato Referencias de la Facade
Illuminate\Contracts\Auth\Access\Authorizable
Illuminate\Contracts\Auth\Access\Gate Gate
Illuminate\Contracts\Auth\Authenticatable
Illuminate\Contracts\Auth\CanResetPassword
Illuminate\Contracts\Auth\Factory Auth
Illuminate\Contracts\Auth\Guard Auth::guard()
Illuminate\Contracts\Auth\PasswordBroker Password::broker()
Illuminate\Contracts\Auth\PasswordBrokerFactory Password
Illuminate\Contracts\Auth\StatefulGuard
Illuminate\Contracts\Auth\SupportsBasicAuth
Illuminate\Contracts\Auth\UserProvider
Illuminate\Contracts\Bus\Dispatcher Bus
Illuminate\Contracts\Bus\QueueingDispatcher Bus::dispatchToQueue()
Illuminate\Contracts\Broadcasting\Factory Broadcast
Illuminate\Contracts\Broadcasting\Broadcaster Broadcast::connection()
Illuminate\Contracts\Broadcasting\ShouldBroadcast
Illuminate\Contracts\Broadcasting\ShouldBroadcastNow
Illuminate\Contracts\Cache\Factory Cache
Illuminate\Contracts\Cache\Lock
Illuminate\Contracts\Cache\LockProvider
Illuminate\Contracts\Cache\Repository Cache::driver()
Illuminate\Contracts\Cache\Store
Illuminate\Contracts\Config\Repository Config
Illuminate\Contracts\Console\Application
Illuminate\Contracts\Console\Kernel Artisan
Illuminate\Contracts\Container\Container App
Illuminate\Contracts\Cookie\Factory Cookie
Illuminate\Contracts\Cookie\QueueingFactory Cookie::queue()
Illuminate\Contracts\Database\ModelIdentifier
Illuminate\Contracts\Debug\ExceptionHandler
Illuminate\Contracts\Encryption\Encrypter Crypt
Illuminate\Contracts\Events\Dispatcher Event
Illuminate\Contracts\Filesystem\Cloud Storage::cloud()
Illuminate\Contracts\Filesystem\Factory Storage
Illuminate\Contracts\Filesystem\Filesystem Storage::disk()
Illuminate\Contracts\Foundation\Application App
Illuminate\Contracts\Hashing\Hasher Hash
Illuminate\Contracts\Http\Kernel
Illuminate\Contracts\Mail\MailQueue Mail::queue()
Illuminate\Contracts\Mail\Mailable
Illuminate\Contracts\Mail\Mailer Mail
Illuminate\Contracts\Notifications\Dispatcher Notification
Illuminate\Contracts\Notifications\Factory Notification
Illuminate\Contracts\Pagination\LengthAwarePaginator
Illuminate\Contracts\Pagination\Paginator
Illuminate\Contracts\Pipeline\Hub
Illuminate\Contracts\Pipeline\Pipeline
Illuminate\Contracts\Queue\EntityResolver
Illuminate\Contracts\Queue\Factory Queue
Illuminate\Contracts\Queue\Job
Illuminate\Contracts\Queue\Monitor Queue
Illuminate\Contracts\Queue\Queue Queue::connection()
Illuminate\Contracts\Queue\QueueableCollection
Illuminate\Contracts\Queue\QueueableEntity
Illuminate\Contracts\Queue\ShouldQueue
Illuminate\Contracts\Redis\Factory Redis
Illuminate\Contracts\Routing\BindingRegistrar Route
Illuminate\Contracts\Routing\Registrar Route
Illuminate\Contracts\Routing\ResponseFactory Response
Illuminate\Contracts\Routing\UrlGenerator URL
Illuminate\Contracts\Routing\UrlRoutable
Illuminate\Contracts\Session\Session Session::driver()
Illuminate\Contracts\Support\Arrayable
Illuminate\Contracts\Support\Htmlable
Illuminate\Contracts\Support\Jsonable
Illuminate\Contracts\Support\MessageBag
Illuminate\Contracts\Support\MessageProvider
Illuminate\Contracts\Support\Renderable
Illuminate\Contracts\Support\Responsable
Illuminate\Contracts\Translation\Loader
Illuminate\Contracts\Translation\Translator Lang
Illuminate\Contracts\Validation\Factory Validator
Illuminate\Contracts\Validation\ImplicitRule
Illuminate\Contracts\Validation\Rule
Illuminate\Contracts\Validation\ValidatesWhenResolved
Illuminate\Contracts\Validation\Validator Validator::make()
Illuminate\Contracts\View\Engine
Illuminate\Contracts\View\Factory View
Illuminate\Contracts\View\View View::make()

Regístrate hoy en Styde y obtén acceso a todo nuestro contenido.

Lección anterior Facades - Documentación de Laravel 6 Lección siguiente Rutas - Documentación de Laravel 6