Преимущество такого паттерна состоит в разделении различной логики вашего приложения. При этом одно событие может иметь много независимых друг от друга слушателей. Например, вы хотите получать каждый раз уведомление о том, что у вас новый зарегистрированный пользователь на сайте. Для этого вам не нужно вносить изменения в класс регистрации пользователей или каким-то образом расширять его. Достаточно создать класс независимого слушателя события "Регистрация" (подписаться на событие), внутри которого и будет реализована логика уведомления.
Регистрация событий и слушателей
Связью событий и слушателей в Laravel занимается класс App\Providers\EventServiceProvider. Laravel предоставляет возможность осуществить эту связку несколькими различными способами, а вы вправе использовать как только один, так и несколько способов одновременно, в зависимости от логики вашего приложения и отдельных его компонентов.
Явная связка
Явный способ позволяет наглядно задать событие и набор классов-обработчиков, реагирующих на это событие при помощи поля $listen вышеупомянутого класса App\Providers\EventServiceProvider:
class EventServiceProvider {
...
protected $listen = [
EventClass::class => [
EventListener::class,
EventListener2::class,
...
],
Event2Class::class => [
Event2Listener::class,
Event2Listener2::class,
...
],
];
Динамическая связка
В отличие от предыдущего способа, мы также имеем возможность связывать обработчик (слушатель) с событием в процессе выполнения при помощи фасада Event, например в методе boot класса EventServiceProvider:
//Обработчиком события MyEvent будет метод handle класса MyEventHandler
Event::listen(
MyEvent::class, [MyEventHandler::class, 'handle']
);
//Обработчиком события MyEvent будет анонимная функция, объявленная внутри аргумента метода Event::listen
Event::listen(function (MyEvent $event) {
//логика обработки
});
Групповая связка
Если мы хотим задать один обработчик сразу на несколько событий, имена которых попадают под указанный паттерн, то мы можем воспользоваться следующей конструкцией:
Event::listen('user.*', function ($eventName, array $data) {
//
});
Автообнаружение слушателей (неявная связка)
В отличие от двух вышеуказанных методов данный способ доступен только в том случае, если внутри класса EventServiceProvider описан метод, дающий Laravel инструкцию для запуска механизма автоматического обнаружения обработчиков, лежащих в каталоге app/Listeners:
public function shouldDiscoverEvents()
{
return true;
}
/**
* Получить каталоги слушателей, которые следует использовать для обнаружения событий.
*
* @return array
*/
protected function discoverEventsWithin()
{
return [
$this->app->path('Listeners'),
];
}
Для того, чтобы понимать, как именно Laravel будет связывать события с обработчиками, необходимо понять структуру класса обработчика. В минимальном варианте класс обработчик (слушатель) должен содержать в себе метод handle:
namespace App\Listeners;
class MyEventHandler {
...
public function handle(MyEvent $event){
//Логика обработки
}
}
Именно благодаря сигнатуре метода handle, указывающей явно на класс события MyEvent, Laravel "поймет", что данный обработчик слушает событие MyEvent.
Для того, чтобы Laravel произвел процедуру связки через автообнаружение и закешировал связь событий и слушателей, можно запустить команду:
php artisan event:cache
Если кеша ранее не было и команда кеширования не запускалась ранее, то кеш будет создан при первом запуске приложения (например при заходе пользователя на веб-страницу).
Для очистки кеша, которая может потребоваться, когда вы создали новые события и новые обработчики, необходимо запустить:
php artisan event:clear
Генерация классов событий и слушателей
Автоматическая генерация классов слушателей и событий
Вы можете сгенерировать заготовки под них при помощи команды
php artisan event:generate
Данная команда создаст классы для тех событий и слушателей, которые перечислены в поле $listen класса EventServiceProvider и классы которых еще не созданы
Самостоятельная генерация классов слушателей и событий
Если вы хотите создать классы событий и слушателей самостоятельно (например для автообнаружения), то используйте следующие команды:
php artisan make:event MyEvent
php artisan make:listener SendPodcastNotification --event=PodcastProcessed
Параметр --event
в команде создания слушателя позволяет автоматически в методе handle указать явно класс события, которое должен слушать данный обработчик (слушатель).
Добавление слушателей в очередь
В определенных ситуациях нам может потребоваться запустить обработчик события через очередь, для того, чтобы не блокировать основной поток приложения и тем самым не замедлять его работу. Механизмы работы с событиями и слушателями в Laravel позволяют это сделать без особых танцев с бубном.
Размещение анонимного обработчика в очередь
Event::listen(queueable(function (MyEvent $event) {
//Логика обработчика
}));
Добавление параметров очереди
Также вы можете задать параметры обработки через очередь:
Event::listen(queueable(function (MyEvent $event) {
//
})->onConnection('redis')->onQueue('myevents')->delay(now()->addSeconds(10)));
Обработка неудачных событий
При возникновении проблем при обработке события, вы также можете перехватывать такие ситуации:
Event::listen(queueable(function (MyEvent $event) {
//
})->catch(function (MyEvent $event, Throwable $e) {
// Событие в очереди завершилось неудачно ...
}));
Запуск класса-обработчика автоматически в очереди
В отличие от анонимных обработчиков, классы-слушатели могут быть обработаны через очередь путем указания имплементации интерфейса Illuminate\Contracts\Queue\ShouldQueue
:
use Illuminate\Contracts\Queue\ShouldQueue;
class MyEventListener implements ShouldQueue {
...
}
При этом параметры очереди можно задать при помощи полей внутри класса:
class MyEventListener implements ShouldQueue {
public $connection = 'redis';
public $queue = 'myevents';
public $delay = 120;
Если вы хотите определить очередь и соединение динамически во время выполнения, используйте методы:
public function viaQueue()
{
return 'myevents';
}
public function viaConnection()
{
return 'redis';
}
Помимо прочего, вы можете динамически определять, стоит ли данный обработчик отправлять в очередь или исполнить его в текущем потоке немедленно:
use Illuminate\Contracts\Queue\ShouldQueue;
class MyEventListener implements ShouldQueue {
...
public function shouldQueue(MyEvent $event)
{
return $event->isTooBig;
}
}
Дополнительные детали обработки неудачных выполнение обработчика можно задать при помощи методов, реализуемых трейтом InteractsWithQueue
Отправка событий (генерация события)
Достаточно вызвать метод dispatch класса события. При этом в качестве параметров передаются те же параметры, которые требуются в конструкторе класса события:
MyEvent::dispatch($param);
Класс-подписчик
Еще один способ сгруппировать логику событий и обработчиков — создать класс-подписчик, который реализует внутри себя логику подписки на одно или несколько событий. Для того, чтобы Laravel мог понять, какие подписчики необходимо запустить, перечислите их в поле $subscribe класса EventServiceProvider:
/**
* Классы подписчиков для регистрации.
*
* @var array
*/
protected $subscribe = [
App\Listeners\MyEventSubscriber::class,
];
Далее определяем класс подписчик:
<?php
namespace App\Listeners;
class MyEventSubscriber
{
public function myEventHandler1($event){
//Некая функция-обработчик события
}
public function myEventHandler2($event){
//Некая вторая функция-обработчик события
}
public function subscribe($events)
{
//Связываем обработчик 1 и событие MyEvent
$events->listen(
MyEvent::class,
[MyEventSubscriber::class, 'myEventHandler1']
);
//Связываем обработчик 2 и событие MyEvent
$events->listen(
MyEvent::class,
[MyEventSubscriber::class, 'myEventHandler2']
);
}
}
Заключение
Как мы выяснили выше, Laravel предоставляет гибкие способы задания событий и слушателей, которые подойдут как для небольших веб-приложений, так и для огромных сервисов. А четкая структуризация при помощи каталогов и пространств имен позволит вам не запутаться в событиях и обработчиках по мере роста вашего приложения.
Успехов в веб-разработке!
Вы должны авторизоваться, чтобы оставлять комментарии.