События и слушатели в Laravel

События Laravel обеспечивают простую реализацию паттерна "Наблюдатель" (Observer), позволяя вам подписываться и отслеживать различные события, происходящие в вашем приложении. Классы событий обычно хранятся в каталоге app/Events, а их слушатели – в app/Listeners, но это не обязательно.

}

Автор материала

. Веб-разработчик, создатель проекта Лектория, эксперт MODX Revolution, директор веб-студии OpenColour. Youtube-канал OpenModx.

10 минут на прочтение
Теги по теме:

Преимущество такого паттерна состоит в разделении различной логики вашего приложения. При этом одно событие может иметь много независимых друг от друга слушателей. Например, вы хотите получать каждый раз уведомление о том, что у вас новый зарегистрированный пользователь на сайте. Для этого вам не нужно вносить изменения в класс регистрации пользователей или каким-то образом расширять его. Достаточно создать класс независимого слушателя события "Регистрация" (подписаться на событие), внутри которого и будет реализована логика уведомления.

Регистрация событий и слушателей

Связью событий и слушателей в 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 предоставляет гибкие способы задания событий и слушателей, которые подойдут как для небольших веб-приложений, так и для огромных сервисов. А четкая структуризация при помощи каталогов и пространств имен позволит вам не запутаться в событиях и обработчиках по мере роста вашего приложения.

Успехов в веб-разработке!

Комментарии

    Вы должны авторизоваться, чтобы оставлять комментарии.

    Мы используем куки на нашем сайте. Продолжая просмотр, вы соглашаетесь с условиями пользовательского соглашения
    Пожалуйста, подождите. Процесс оформления заказа может занимать до 30 секунд.