Что такое contracts в laravel
Перейти к содержимому

Что такое contracts в laravel

  • автор:

Контракты

Контракты в Laravel — это набор классов-интерфейсов, определяющий некий функционал ядра фреймворка. Например, контракт Queue определяет методы работы с очередями, Mailer — методы для отправки мейлов.

Каждый контракт имеет свою реализацию (implementation) во фреймворке. Например, есть реализация Queue с различными драйверами очередей и реализация Mailer с использованием SwiftMailer.

Для удобства, контракты находятся в отдельном репозитории на гитхабе — https://github.com/illuminate/contracts.

Зачем нужны контракты?

Вы можете спросить — а для чего нужны контракты? Зачем вообще нужны интерфейсы?

Ответ — слабая связность и упрощение кода.

Слабая связность

Сначала давайте рассмотрим пример кода с сильной связностью.

 namespace App\Orders; class Repository < /** * The cache. */ protected $cache; /** * Create a new repository instance. * * @param \Package\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)) < // > > > 

Код этого класса тесно связан с реализацией кэширования \SomePackage\Cache\Memcached . Мы зависим и от способа кэширования ( memcached ), и от API данной библиотеки. Если мы хотим сменить кэширование с memcached на redis , нам придется вносить изменения в код класса Repository .

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

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

Этот код не связан ни с одной внешней библиотекой, в том числе с ядром фреймворка! Контракт не содержит никакой конкретной реализации кэширования, только интерфейс, и вы можете написать любую свою реализацию кэширования — используя внешние библиотеки или нет. Кроме того, теперь вы можете в любой момент легко изменить способ кэширования, просто подав другую реализацию в конструктор класса при регистрации его в сервис-контейнере в методе register() вашего сервис-провайдера.

Упрощение кода

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

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

Таблица основных контрактов

Вот таблица соответствий контрактов Laravel 5 фасадам Laravel 4 :

Контракт Фасад Laravel 4.x
Illuminate\Contracts\Auth\Guard Auth
Illuminate\Contracts\Auth\PasswordBroker Password
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

Использование контрактов

Как получить в своем классе реализацию заданного контракта? Очень просто. Достаточно в аргументах конструктора явно указать в качестве типа аргумента название соответствующего контракта — и при его создании там окажется реализация этого интерфейса. Это происходит за счет того, что почти все классы Laravel (контроллеры, слушатели событий и очередей, роуты, фильтры роутов) регистрируются в сервис-контейнере и в процессе создания экземпляра класса из контейнера в него встраиваются все определенные таким образом зависимости. Фактически все происходит автоматически, вам не приходится задумываться об этом механизме.

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

 namespace App\Events; use App\User; use Illuminate\Contracts\Queue\Queue; class NewUserRegistered < /** * The queue implementation. */ protected $queue; /** * Create a new event listener instance. * * @param Queue $queue * @return void */ public function __construct(Queue $queue) < $this->queue = $queue; > /** * Handle the event. * * @param User $user * @return void */ public function fire(User $user) < // Queue an e-mail to the user. > > 

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

Русскоязычное комьюнити
  • Группа в VK
  • Телеграм LaravelRUS
  • Телеграм Laravel для новичков
  • Телеграм LaravelPro
Обучающие ресурсы
  • Laracasts
  • Codecourse
  • Курс Дмитрия Елисеева
  • Adam Wathan
Блоги разработчиков
  • Laravel News
  • Freek Van der Herten
  • Brent Roose
  • Marcel Pociot

Контракты

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

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

Все контракты Laravel находятся в их собственном репозитории GitHub. Это обеспечивает быстрый доступ к списку всех доступных контрактов, а также единый, отдельный пакет, который используется разработчиками пакетов, взаимодействующих со службами Laravel.

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

Фасады и глобальные помощники Laravel обеспечивают простой способ использования служб Laravel без объявления типов зависимости и извлечения их реализаций из контейнера служб. В большинстве случаев каждый фасад имеет эквивалентный контракт.

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

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

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

В общем, большинство приложений могут без проблем использовать фасады во время разработки. Если вы создаете пакет, который интегрируется с несколькими фреймворками PHP, то вы можете указать пакет illuminate/contracts в файле composer.json вашего пакета для определения вашей интеграции со службами Laravel без необходимости требований конкретных реализаций Laravel.

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

Итак, как получить реализацию контракта? На самом деле это довольно просто.

Многие типы классов в Laravel извлекаются из контейнера служб, включая контроллеры, слушателей событий, посредники, очереди заданий и даже замыкания маршрутов. Итак, чтобы получить реализацию контракта, вы можете просто «объявить тип» интерфейса в конструкторе извлекаемого класса.

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

 namespace App\Listeners; use App\Events\OrderWasPlaced; use App\Models\User; use Illuminate\Contracts\Redis\Factory; class CacheOrderInformation < /** * Реализация фабрики Redis. * * @var \Illuminate\Contracts\Redis\Factory */ protected $redis; /** * Создать новый экземпляр обработчика события. * * @param \Illuminate\Contracts\Redis\Factory $redis * @return void */ public function __construct(Factory $redis) < $this->redis = $redis; > /** * Обработать событие. * * @param \App\Events\OrderWasPlaced $event * @return void */ public function handle(OrderWasPlaced $event) < // > > 

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

Справочник контрактов

В этой таблице содержится краткий справочник по всем контрактам Laravel и их эквивалентным фасадам:

Контракт Фасад
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()
Русскоязычное комьюнити
  • Группа в VK
  • Телеграм LaravelRUS
  • Телеграм Laravel для новичков
  • Телеграм LaravelPro
Обучающие ресурсы
  • Laracasts
  • Codecourse
  • Курс Дмитрия Елисеева
  • Adam Wathan
Блоги разработчиков
  • Laravel News
  • Freek Van der Herten
  • Brent Roose
  • Marcel Pociot

Contracts

Laravel’s «contracts» are a set of interfaces that define the core services provided by the framework. For example, an 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.

Each contract has a corresponding implementation provided by the framework. For example, Laravel provides a queue implementation with a variety of drivers, and a mailer implementation that is powered by Symfony Mailer.

All of the Laravel contracts live in their own GitHub repository. This provides a quick reference point for all available contracts, as well as a single, decoupled package that may be utilized when building packages that interact with Laravel services.

Contracts Vs. Facades

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.

Unlike facades, which do not require you to require them in your class’ constructor, contracts allow you to define explicit dependencies for your classes. Some developers prefer to explicitly define their dependencies in this way and therefore prefer to use contracts, while other developers enjoy the convenience of facades. In general, most applications can use facades without issue during development.

When To Use Contracts

The decision to use contracts or facades will come down to personal taste and the tastes of your development team. Both contracts and facades can be used to create robust, well-tested Laravel applications. Contracts and facades are not mutually exclusive. Some parts of your applications may use facades while others depend on contracts. As long as you are keeping your class’ responsibilities focused, you will notice very few practical differences between using contracts and facades.

In general, most applications can use facades without issue during development. If you are building a package that integrates with multiple PHP frameworks you may wish to use the illuminate/contracts package to define your integration with Laravel’s services without the need to require Laravel’s concrete implementations in your package’s composer.json file.

How To Use Contracts

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. So, to get an implementation of a contract, you can just «type-hint» the interface in the constructor of the class being resolved.

For example, take a look at this event listener:

 
namespace App\Listeners;
use App\Events\OrderWasPlaced;
use App\Models\User;
use Illuminate\Contracts\Redis\Factory;
class CacheOrderInformation
/**
* Create a new event handler instance.
*/
public function __construct(
protected Factory $redis,
) <>
/**
* Handle the event.
*/
public function handle(OrderWasPlaced $event): void
// .
>
>

When the event listener is resolved, the service container will read the type-hints on the constructor of the class, and inject the appropriate value. To learn more about registering things in the service container, check out its documentation.

Contract Reference

This table provides a quick reference to all of the Laravel contracts and their equivalent facades:

Contract References 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 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()

Laravel is a web application framework with expressive, elegant syntax. We believe development must be an enjoyable and creative experience to be truly fulfilling. Laravel attempts to take the pain out of development by easing common tasks used in most web projects.

Продвинутый Laravel: Контракты и Реализации

Контракты и реализации — это мощные инструменты в Laravel, позволяющие определять стандартный интерфейс и писать код, который можно адаптировать к различным реализациям.

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

Контракт похож на деловое соглашение в том смысле, что это соглашение между сторонами. Контракты обычно имеют условия, которые являются условиями соглашения, которым обе стороны соглашаются следовать. Когда мы берём это и переводим в код, у нас получается что-то вроде этого:

namespace App\Contracts; 

interface Dvr

public function play();

public function pause();
>

В этом контракте мы соглашаемся вести дела с DVR и условиями (методами) контракта, которые мы должны соблюдать, чтобы play() и pause() DVR. Как вы можете себе представить, есть несколько компаний предоставляющих услуги DVR. В Соединённых Штатах двумя крупнейшими DVR поставщиками являются Honeywell и Haydon.

Теперь давайте создадим службу API для Honeywell и Haydon.

app/Services/HoneywellApi.php:

namespace App\Services; 

class HoneywellApi

public function pressPlay()

return 'Play Honeywell DVR';
>

public function pressPause()

return 'Pause Honeywell DVR';
>
>

app/Services/HaydonApi.php:

namespace App\Services; 

class HaydonApi

public function play()

return 'Play Haydon DVR';
>

public function pause()

return 'Pause Haydon DVR';
>
>

Вы. вероятно, заметили, что имена методов, используемые HoneywellApi , отличаются от имён методов HaydonApi , но это совершенно нормально. Почему? Потому что мы не можем сделать всех поставщиков API и SDK одинаковыми. Теперь мы создадим реализацию для предоставления каждого API, соблюдая при этом контракт.

app/Implementations/Honeywell.php:

namespace App\Implementations; 

use App\Contracts\Dvr;
use App\Services\HoneywellApi;

class Honeywell implements Dvr

public function __construct(protected HoneywellApi $api) >

public function play()

return $this->api->pressPlay();
>

public function pause()

return $this->api->pressPause();
>
>

app/Implementations/Haydon.php:

namespace App\Implementations; 

use App\Contracts\Dvr;
use App\Services\HaydonApi;

class Haydon implements Dvr

public function __construct(protected HaydonApi $api)>

public function play()

return $this->api->play();
>

public function pause()

return $this->api->pause();
>
>

Для использования одного из этих провайдеров, нужно создать контроллер вместе с маршрутом, указывающим на контроллер. Как вы скоро увидите, мы внедрим interface в конструктор. Это важная часть, поскольку она позволяет нам переключаться между поставщиками API, если что-нибудь понадобится. (Позже мы воспользуемся сервис-контейнером Laravel, чтобы собрать всё вместе)

app/Http/Controllers/DvrController.php:

namespace App\Http\Controllers; 

use App\Contracts\Dvr;

class DvrController extends Controller

public function __construct(protected Dvr $dvr)>

public function play()

return $this->dvr->play();
>

public function pause()

return $this->dvr->pause();
>
>

Контроллер заботиться только о воспроизведении или приостановке DVR, его не волнует (и не должно заботить) то, кто будет предоставлять базовую услугу.

Двигаясь дальше, мы создадим маршрут для каждого метода контроллера в файле маршрутов api.php .

routes/api.php:

use Illuminate\Support\Facades\Route; 

Route::get(‘dvr/play’, [\App\Http\Controllers\DvrController::class, ‘play’]);

Route::get(‘dvr/pause’,[\App\Http\Controllers\DvrController::class, ‘pause’]);

Когда мы запускаем браузер и переходим к нашему маршруту воспроизведения ( http://example.dev/api/dvr/play ), мы сталкиваемся со следующим:

Illuminate\Contracts\Container\BindingResolutionException 
Target [App\Contracts\Dvr] is not instantiable while building [App\Http\Controllers\DvrController].

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

Давайте немного повеселимся. Мы войдём в наш AppServiceprovider и скажем Laravel, что когда приложение ищет реализацию Dvr, мы вернём ему реализацию Honeywell.

app/Providers/AppServiceProvider.php:

namespace App\Providers; 

use Illuminate\Support\ServiceProvider;

class AppServiceProvider extends ServiceProvider

/**
* Register any application services.
*/

public function register(): void

$this->app->bind(
\App\Contracts\Dvr::class,
\App\Implementations\Honeywell::class
);
>

/**
* Bootstrap any application services.
*/

public function boot(): void

//
>
>

Теперь мы вернёмся на страницу, которую только что посещали, и обновим.

Play Honeywell DVR

Довольно круто, да? Теперь давайте посетим маршрут паузы ( http://example.dev/api/dvr/pause ), который возвращает:

Pause Honeywell DVR

Теперь предположим, что ваш менеджер приходит и заявляет, что мы собираемся перейти с Honeywell на Haydon, вам нужно просто обновить AppServiceProvider до:

app/Providers/AppServiceProvider.php:

namespace App\Providers; 

use Illuminate\Support\ServiceProvider;

class AppServiceProvider extends ServiceProvider

/**
* Register any application services.
*/

public function register(): void

$this->app->bind(
\App\Contracts\Dvr::class,
\App\Implementations\Haydon::class
);
>

/**
* Bootstrap any application services.
*/

public function boot(): void

//
>
>

Посетим страницу маршрута воспроизведения ( http://example.dev/api/dvr/play ), который возвращает:

Play Haydon DVR

Теперь маршрут паузы ( http://example.dev/api/dvr/pause ) возвращает:

Pause Haydon DVR

Теперь давайте продвинемся ещё дальше. Представьте себе, что менеджер возвращается и говорит: Итак, мы говорили об этом, на самом деле мы хотим использовать как Honeywell, так и Haydon. Вы можете это сделать? А вы говорите: Я вас прикрою, босс!

Учитывая, что теперь мы будем предоставлять несколько поставщиков, давайте применим DRY к нашим контроллерам.

app/Http/Controllers/HaydonController.php:

namespace App\Http\Controllers; 

class HaydonController extends DvrController >

app/Http/Controllers/HoneywellController.php:

namespace App\Http\Controllers; 

class HoneywellController extends DvrController >

Как видите, они оба расширяют изначально созданный нами DvrController .

Давайте обновим файл маршрутов api.php , чтобы они соответствовали им:

routes/api.php:

use Illuminate\Support\Facades\Route; 

Route::get('dvr/play', [\App\Http\Controllers\DvrController::class, 'play']);
Route::get('dvr/pause', [\App\Http\Controllers\DvrController::class, 'pause']);

Route::get('dvr/play/honeywell', [\App\Http\Controllers\HoneywellController::class, 'play']);
Route::get('dvr/pause/honewell', [\App\Http\Controllers\HoneywellController::class, 'pause']);

Route::get('dvr/play/haydon', [\App\Http\Controllers\HaydonController::class, 'play']);
Route::get('dvr/pause/haydon', [\App\Http\Controllers\HaydonController::class, 'pause']);

Когда мы посещаем конечную точку воспроизведения Honeywell ( http://laravelcontractsandimplementations.test/api/dvr/play/honeywell ), сталкиваемся с проблемой:

Play Haydon DVR

Сейчас, Laravel знает, что когда запрашиваем Dvr , мы возвращаем реализацию Haydon .

Итак, как нам это решить? Нужно предоставить AppServiceProvider дополнительный контекст:

app/Providers/AppServiceProvider.php:

namespace App\Providers; 

use Illuminate\Support\ServiceProvider;

class AppServiceProvider extends ServiceProvider

/**
* Register any application services.
*/

public function register(): void

$this->app->bind(
\App\Contracts\Dvr::class,
\App\Implementations\Haydon::class
);

$this->app
->when(\App\Http\Controllers\HoneywellController::class)
->needs(\App\Contracts\Dvr::class)
->give(\App\Implementations\Honeywell::class);

$this->app
->when(\App\Http\Controllers\HaydonController::class)
->needs(\App\Contracts\Dvr::class)
->give(\App\Implementations\Haydon::class);
>

/**
* Bootstrap any application services.
*/

public function boot(): void

//
>
>

Теперь, когда мы посещаем конечный точки для Honeywell или Haydon, получаем правильные данные.

В заключение, контракты и реализации — это мощные инструменты в Laravel, позволяющие определять стандартный интерфейс и писать код, который можно адаптировать к различным реализациям. Используя контракты, вы можете создавать более модульный, масштабируемый и удобный в сопровождении код, который при необходимости можно легко обновлять или заменять другими реализациями. Я надеюсь, что эта статья была информативной и полезной в вашем путешествии по Laravel. И я призываю вас продолжать изучать и экспериментировать с этим увлекательным фреймворком!

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *