Contracts

Introdução

As Contracts do Laravel são uma série de interfaces que definem os serviços essenciais fornecidos pelo framework. Por exemplo, a contract Illuminate\Contracts\Queue\Queue define os metodos para trabalhar com filas, enquanto a contract Illuminate\Contracts\Mail\Mailer define os métodos necessários para o envio de emails.

Cada contract possui uma implementação correspondente disponibilizada pelo framework. Por exemplo, o Laravel disponibiliza uma implementação de queue para inúmeros drivers, e uma implementação do mailer fornecida pelo SwiftMailer.

Todas as contracts do Laravel estão em um repositório no GitHub. Isto fornece um ponto de referência rápida para todas as contracts disponíveis, assim como um pacote único, dissociado dos demais, que pode ser utilizado por desenvolvedores de pacotes.

Contracts Vs. Facades

As facades do Laravel fornecem um modo simples de utilizar os serviços do framework, sem a necessidade de explicitar e resolver as contracts através do service container. Entretanto, utilizar contracts permite que você defina explicitamente as dependências de sua classe. Para a maioria das aplicações as Facades são o sufuciente. Porém, caso seja necessário o baixo acoplamente fornecido pelos contracts, continue lendo!

Por que Contracts?

Você pode ter muitas perguntas a respeito de contracts. Por que utilizar interfaces? Utilizar interfaces não é mais complicado? Vamos exemplificar as razões para o uso de interfaces para os seguintes tópicos: Simplicidade e Baixo Acoplamento.

Baixo Acoplamento

Primeiro, vamos revisar um código que está fortemente vinculado a uma implementação de cache. Considere o seguinte:

<?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)) {
            //
        }
    }
}

Nesta classe, o código está fortemente vinculado a uma implementação de cache. Isto se deve ao fato de que estamos dependendo de uma classe concreta de Cache. Se a API desta classe for alterada, termos que alterar nosso código também.

Do mesmo modo, se for necessário alterar a atual tecnologia de cache (Memcached) por outra (Redis), teremos que modificar nosso repositório novamente. O repositório não dever ter tanto conhecimento a respeito de quem irá provê-lo de dados ou mesmo como eles estão sendo providos.

Ao invés desta abordagem, podemos melhorar nosso código dependendo de uma interface simples e vendor agnostic:

<?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;
    }
}

Agora o código não está vinculado a um vendor específico, nem mesmo ao Laravel. Já que o pacote de contracts não contém implementações e dependências, você pode facilmente escrever uma implementação alternativa para qualquer uma das contracts, possibilitando que você troque a implementação do cache sem que seja necessário alterações em seu código.

Simplicidade

Quando todos os serviços do Laravel estão nitidamente definidos em interfaces simples, é muito fácil determinar a funcionalidade oferecida pelo serviço. As contracts servem como uma documentação sucinta dos recursos do framework.

Além disso, quando você depende de interfaces simples, o entendimento e a manutenção do seu código são mais fáceis. Ao invés de rastrear quais métodos estão disponíveis dentro de uma extensa e complexa classe, você pode consultar uma interface simples e limpa.

Referência das Contracts

Esta é uma referência para a maiorias das Contracts do Laravel, assim como suas respectivas Facades:

Contract Facade
Illuminate\Contracts\Auth\Guard Auth
Illuminate\Contracts\Auth\PasswordBroker Password
Illuminate\Contracts\Bus\Dispatcher Bus
Illuminate\Contracts\Broadcasting\Broadcaster  
Illuminate\Contracts\Cache\Repository Cache
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 Mail
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  

Como Utilizar Contracts

Então, como você consegue uma implementação de uma contract? Na verdade é bastante simples.

Muitos tipos de classes no Laravel são resolvidos através do service container, incluindos os controllers, event listeners, middlewares, queue jobs, e até route Closures. Logo, para obter a implementação de uma contract, basta você "informar" a interface desejada no construtor da classe que está sendo resolvida.

Por exemplo, considere o seguinte 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;

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

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

Quando o event listener é resolvido, o service container lerá o construtor da classe e injetará as dependências apropriadas. Para aprender mais sobre como registrar coisas no service container dê uma olhada na documentação.