Введение
Контракты в Laravel — это набор интерфейсов, которые описывают основной функционал, предоставляемый фреймворком. Например, контракт Illuminate\Contracts\Queue\Queue определяет методы, необходимые для организации очередей, в то время как контракт Illuminate\Contracts\Mail\Mailer определяет методы, необходимые для отправки электронной почты.
Каждый контракт имеет свою реализацию во фреймворке. Например, Laravel предоставляет реализацию PHPQueue
с различными драйверами и реализацию PHPMailer
, использующую SwiftMailer.
Все контракты Laravel живут в своих собственных репозиториях GitHub. Эта ссылка ведёт на все доступные контракты, а также на один отдельный пакет, который может быть использован разработчиками пакетов.
Контракты или фасады?
Фасады Laravel и вспомогательные функции дают простой способ использования сервисов Laravel без необходимости типизирования и извлечения контрактов из сервис-контейнера. В большинстве случаев у каждого фасада есть эквивалентный контракт.
В отличие от фасадов, которые не требуют того, чтобы вы запрашивали их в конструкторе вашего класса, контракты позволяют вам определить конкретные зависимости для ваших классов. Некоторые разработчики предпочитают именно так явно определять свои зависимости, поэтому предпочитают использовать контракты, а другие разработчики наслаждаются удобством фасадов.
Для большинства приложений неважно, что вы выберете — фасады или контракты. Но если вы создаёте пакет, то вам надо использовать контракты, так как в этом случае их проще тестировать.
Когда использовать контракты
Это обсуждается повсюду, и большинство дискуссий сводятся к тому, что использование контрактов или фасадов — это дело вкуса или предпочтений вашей команды разработчиков. И те, и другие можно использовать для создания надёжных, проверенных Laravel-приложений. Пока вы сохраняете границы ответственности вашего класса узкими, вы сможете заметить всего несколько практических различий между использованием контрактов и фасадов.
Однако, у вас по-прежнему могут остаться некоторые вопросы о контрактах. Например, зачем вообще нужны интерфейсы? Разве их использование делает жизнь проще? Определим причины использования интерфейсов как следующие: это слабая связанность (loose coupling) и упрощение кода.
Слабая связанность
Но для начала рассмотрим код с сильной связанностью с реализацией кэша. Рассмотрите следующее:
<?php
namespace App\Orders;
class Repository {
/**
* Экземпляр кэша.
*/
protected $cache;
/**
* Создание нового экземпляра репозитория.
*
* @param \SomePackage\Cache\Memcached $cache
* @return void
*/
public function __construct(\SomePackage\Cache\Memcached $cache)
{
$this->cache = $cache;
}
/**
* Получение заказа по ID.
*
* @param int $id
* @return Order
*/
public function find($id)
{
if ($this->cache->has($id)) {
//
}
}
}
В этом классе код сильно связан с реализацией кэша, потому что мы зависим от конкретного класса Cache данного пакета. Если API этого пакета изменится, наш код должен также измениться.
Аналогично, если мы хотим заменить нашу базовую технологию кэша (Memcached) другой технологией (Redis), нам придётся вносить изменения в наш репозиторий. А наш репозиторий не должен задумываться о том, кто именно предоставляет данные или как он это делает.
Вместо такого подхода, мы можем улучшить наш код, добавив зависимость от простого интерфейса, которой не зависит от поставщика:
<?php
namespace App\Orders;
use Illuminate\Contracts\Cache\Repository as Cache;
class Repository
{
/**
* Экземпляр кэша.
*/
protected $cache;
/**
* Создание нового экземпляра репозитория.
*
* @param Cache $cache
* @return void
*/
public function __construct(Cache $cache)
{
$this->cache = $cache;
}
}
Теперь код не привязан к какому-либо определённому поставщику, и даже не привязан к Laravel. Контракт не содержит никакой конкретной реализации и никаких зависимостей. Вы можете легко написать свою реализацию любого контракта, что позволяет вам заменить реализацию работы с кэшем, не изменяя ни одной строчки вашего кода, работающего с кэшем.
Упрощение кода
Когда все сервисы Laravel аккуратно определены в простых интерфейсах, очень легко определить функциональность, предлагаемую данными сервисами. Фактически, контракты являются краткой документацией для функций Laravel.
Кроме того, когда в своём приложении вы внедряете в классы зависимости от простых интерфейсов, в вашем коде легче разобраться и его проще поддерживать. Вместо того, чтобы искать методы в большом и сложном классе, вы можете обратиться к простому и понятному интерфейсу.
Как использовать контракты
Как получить реализацию контракта? Это довольно просто. Множество типов классов в Laravel регистрируются в сервис-контейнере, включая контроллеры, слушатели событий, посредники, очереди и даже замыкания. Поэтому, чтобы получить реализацию контракта, вам достаточно указать тип интерфейса в конструкторе необходимого класса. Например, посмотрите на этот обработчик событий:
<?php
namespace App\Listeners;
use App\User;
use App\Events\OrderWasPlaced;
use Illuminate\Contracts\Redis\Database;
class CacheOrderInformation
{
/**
* Реализация базы данных Redis.
*/
protected $redis;
/**
* Создание нового экземпляра обработчика событий.
*
* @param Database $redis
* @return void
*/
public function __construct(Database $redis)
{
$this->redis = $redis;
}
/**
* Обработка события.
*
* @param OrderWasPlaced $event
* @return void
*/
public function handle(OrderWasPlaced $event)
{
//
}
}
Когда будет получен слушатель события, сервис-контейнер прочитает указание типа в конструкторе класса и внедрит нужное значение. Узнать больше о регистрации в сервис-контейнере можно в документации.
Список контрактов
В этой таблице приведены ссылки на все контракты Laravel, а также эквивалентные им фасады:
Комментарии (2)
Кажется, тут немного с переводом ошиблись. Причем довольно эпично.
Low Coupling — это слабая связанность, а не связность. Можно перевести как слабая зависимость или слабая связь между классами (модулями).
А то у вас получилось наоборот.
Должна быть слабая связанность (зависимость) Low Coupling между классами (модулями)
И высокая внутренняя связность High Cohesion внутри класса (модуля)
Верно, в переводе ошибка. Спасибо за сообщение.