Контракты

Содержание:


Контракты

Laravel контракты — это набор интерфейсов, которые определяют ядро сервисов, предоставленных фреймворком. Например, контракт Illuminate\Contracts\Queue\Queue определяет методы, необходимы для очереди заданий, в то время как контракт Illuminate\Contracts\Mail\Mailer определяет методы необходимые для отправки почты.

Каждый контракт имеет соответсвующую реализацию фреймворка. Например, Ларавел предоставляет реализацию очередей с большим количеством драйверов и реализацию почты от SwiftMailer.

Все контракты Ларавел доступны их репозитории GitHub. Тут собраны все доступные контракты, а также пакет, который может быть использован разработчиками компонентов.

Контракты или Фасады

Laravel's facades and helper functions provide a simple way of utilizing Laravel's services without needing to type-hint and resolve contracts out of the service container. In most cases, each facade has an equivalent contract.

В отличие от фасадов, контракты позволяют определять явные зависимости для ваших классов. Одни разработчики предпочитают явно определять зависимости и пути использования в контрактах, в то время как другие являются сторонниками удобства фасадов.

Для большинства приложений нет разницы что предпочитаете вы: фасады или контракты. Однако, если собираетесь делать копонент, вам лучше использовать контракты, в силу того, что они проще в тестировании.

Когда использовать контракты

Как и говорилось во многих источниках, решение использовать контракты или фасады зависит от персональных предпочтений или предпочтений команды. И тот и другой стиль разработки позволяет делать качественные, хорошо протестированные приложения. Со временем, вы заметите, что не такая уж и большая разница между этими двумя подходами.

Однако, всё еще есть некоторые вопросы относительно контрактов. Например, зачем использовать интерфейсы вовсе? Может быть это слишком сложно? Давайте разберём причины использования интерфейсов, чтобы стал простым и очевидным:

Слабая связь

Давайте рассмотрим пример кода:

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

В этом классе код тесно связан с реализацией кэша, потому что он привязан к конкретному классу поставщика дополнения. Если что-то поменяется у поставщика, необходимо будет менять и ваш код тоже.

Аналогично, если мы хотим сделать замену на другую технологию кэша(поменять Memcached на Redis), нам опять необходимо менять наш репозиторий. Наш репозиторий не должен иметь так много функционала: кто предоставляет данные, как это происходит и так далее.

Вместо такого подхода, мы можем сделать элегантную замену на независимый от вендора интерфейс:

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

Теперь код не связан ни с поставщиком, ни даже с Ларавел. Поскольку контракты не содержат реализации и зависимостей, вы можете легко написать альтернативную реализацию для любого контракта, что позволяет вам заменить реализацию кэша без изменения контроллера.

Упрощение

Когда сервисы Laravel чётко определена в простых интерфейсах, определение функциональности для данного сервиса становится очень простым. Можно сказать, что контракты служат лаконичной документацией к конмпонентам фреймворка

Дополнительно, когда вы определили зависимости от интерфейсо, ваш код становится более читаемый, легко воспринимаемый и удобен в поддержке. Вместо того чтобы взаимодействовать с методами, представленных в больших и сложных классах, вы можете использовать простые и понятные интерфейсы.


Как использовать контракты

Итак, как получить реализацию контракта? Это очень просто.

Большое количетво типов классов Laravel внедряются черзе сервис контейнеры, включая контроллеры, слушатели событий, средний слой, очереди заданий и даже замыкания маршрута. Чтобы получить реализацию контракта, вам необходимо просто вписать интрефейс в конструктор класса.

Например, взглянем на этого слушателя событий:

<?php

namespace App\Listeners;

use App\User;
use App\Events\OrderWasPlaced;
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)
    {
        //
    }
}

Когда слушатель событый сработает, сервис контейнер прочитает вписанную информацию конструктора класса и внедрит соответствующие значения. О сервис котейнерах и методах взаимодействия с ними, можно прочитать в соответствующем разделе.


Ссылка на контракт

Эта таблица предоставляет быстрые ссылки на контракты Ларавел и соответствующие фасады.

Контрак Соответсвующий фасад
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()