33-35. События
Введение
События в Laravel представлены реализацией паттерна Observer, что позволяет вам подписываться и прослушивать события вашего приложения. Как правило, классы событий находятся в папке app/Events, а классы обработчиков событий — в app/Listeners.
Регистрация событий / слушателей
Сервис-провайдер EventServiceProvider, включённый в ваше Laravel приложение, предоставляет удобное место для регистрации всех слушателей событий. Свойство listen содержит массив всех событий (ключей) и их слушателей (значения). Конечно, вы можете добавить столько событий в этот массив, сколько требуется вашему приложению. Например, давайте добавим событие PodcastWasPurchased:
/** * Слушатель события в вашем приложении. * * @var array */ protected $listen = [ 'App\Events\PodcastWasPurchased' => [ 'App\Listeners\EmailPurchaseConfirmation', ], ];
Генерация классов событий / слушателей
Конечно, вручную создавать файлы для каждого события и слушателя затруднительно. Вместо этого добавьте слушателей и события в ваш EventServiceProvider и используйте команду event:generate
. Эта команда сгенерирует все события и слушателей, которые перечислены в вашем EventServiceProvider.
Конечно, уже существующие события и слушатели останутся нетронутыми:
php artisan event:generate
Регистрация событий вручную
Как правило, события должны регистрироваться через массив $listen EventServiceProvider. Однако, также вы можете регистрировать события вручную с обработчиком событий, используя либо фасад Event, либо реализацию контракта Illuminate\Contracts\Events\Dispatcher
/** * Регистрация своих событий в приложении. * * @param \Illuminate\Contracts\Events\Dispatcher $events * @return void */ public function boot(DispatcherContract $events) { parent::boot($events); $events->listen('event.name', function ($foo, $bar) { // }); }
Слушатели событий по маске
Вы даже можете регистрировать слушателей, используя символ *, что позволит вам поймать несколько событий для одного слушателя. Такой метод вернёт весь массив данных событий одним параметром:
$events->listen('event.*', function (array $data) { // });
Определение событий
Класс события — это просто контейнер данных, содержащий информацию, которая относится к событию. Например, предположим, что наше сгенерированное событие PodcastWasPurchased принимает объект Eloquent ORM:
<?php namespace App\Events; use App\Podcast; use App\Events\Event; use Illuminate\Queue\SerializesModels; class PodcastWasPurchased extends Event { use SerializesModels; public $podcast; /** * Создание нового экземпляра события. * * @param Podcast $podcast * @return void */ public function __construct(Podcast $podcast) { $this->podcast = $podcast; } }
Как видите, этот класс события не содержит функционала. Это просто контейнер для объекта Podcast. Типаж SerializesModels, используемый событием, корректно сериализирует любые Eloquent модели, если объект события будет сериализирован php-функцией serialize
()
.
Определение слушателей
Теперь давайте взглянем на слушателя для нашего примера события. Слушатели событий принимают экземпляр события в свой метод handle
()
. Команда event:generate
автоматически импортирует класс события и указывает тип события в метод handle
()
. В методе handle
()
вы можете выполнять любую логику, необходимую для ответа на событие.
<?php namespace App\Listeners; use App\Events\PodcastWasPurchased; // для версии 5.1 и ранее: // use Illuminate\Queue\InteractsWithQueue; // use Illuminate\Contracts\Queue\ShouldQueue; class EmailPurchaseConfirmation { /** * Создание слушателя события. * * @return void */ public function __construct() { // } /** * Обработка события. * * @param PodcastWasPurchased $event * @return void */ public function handle(PodcastWasPurchased $event) { // Доступ к podcast, используя $event->podcast... } }
Ваши слушатели события могут также указывать тип любых зависимостей, которые необходимы для их конструкторов. Все слушатели события доступны через сервис-контейнер Laravel, поэтому зависимости будут инъецированы автоматически:
use Illuminate\Contracts\Mail\Mailer; public function __construct(Mailer $mailer) { $this->mailer = $mailer; }
Остановка распространения события
Иногда, вам необходимо остановить распространение события для других слушателей. Вы можете сделать это, возвратив false из метода handle
()
вашего слушателя.
Слушатели события в очереди
Необходимо поставить слушателя события в очередь? Проще некуда. Просто добавьте интерфейс ShouldQueue к классу слушателя. Слушателям, сгенерированным Artisan командой event:generate
, уже импортирован этот интерфейс в текущее пространство имен. Так что вы можете сразу использовать его:
<?php namespace App\Listeners; use App\Events\PodcastWasPurchased; // для версии 5.1 и ранее: // use Illuminate\Queue\InteractsWithQueue; use Illuminate\Contracts\Queue\ShouldQueue; class EmailPurchaseConfirmation implements ShouldQueue { // }
Вот и всё! Теперь, когда этого слушателя вызывают для события, он будет автоматически поставлен в очередь диспетчером события, использующим систему очереди Laravel. Если никакие исключения не будут выброшены, когда слушатель выполняется из очереди, то задача в очереди будет автоматически удалена после своего выполнения.
Ручной доступ к очереди
Если вам необходимо получить доступ к базовым методам очереди delete
()
и release
()
вручную, вы можете сделать так. Типаж Illuminate\Queue\InteractsWithQueue, который импортирован по умолчанию в сгенерированные слушатели, предоставляет вам доступ к этим методам:
<?php namespace App\Listeners; use App\Events\PodcastWasPurchased; use Illuminate\Queue\InteractsWithQueue; use Illuminate\Contracts\Queue\ShouldQueue; class EmailPurchaseConfirmation implements ShouldQueue { use InteractsWithQueue; public function handle(PodcastWasPurchased $event) { if (true) { $this->release(30); } } }
Запуск событий
Чтобы запустить событие, вы можете использовать фасад Event, передав экземпляр события методу fire
()
. Метод fire
()
распространит событие для всех его зарегистрированных слушателей:
<?php namespace App\Http\Controllers; use Event; use App\Podcast; use App\Events\PodcastWasPurchased; use App\Http\Controllers\Controller; class UserController extends Controller { /** * Показать профиль заданного пользователя. * * @param int $userId * @param int $podcastId * @return Response */ public function purchasePodcast($userId, $podcastId) { $podcast = Podcast::findOrFail($podcastId); // Логика покупки podcast... Event::fire(new PodcastWasPurchased($podcast)); } }
Также вы можете использовать глобальную вспомогательную функцию event
()
для запуска события:
event(new PodcastWasPurchased($podcast));
Широковещательные события
Во многих современных веб-приложениях используются веб-сокеты, чтобы реализовать быстро обновляющиеся пользовательские интерфейсы реального времени. Когда некоторые данные обновлены на сервере, сообщение обычно отправляется по websocket соединению, которое будет обработано клиентом.
Чтобы помочь вам в создании этих типов приложений, Laravel упрощает «передачу» ваших событий по websocket соединению. Широковещательные события Laravel позволяют вам совместно использовать те же имена событий между серверным кодом и клиентской платформой JavaScript.
Конфигурация
Все параметры широковещательных событий находятся в конфигурационном файле config/broadcasting.php. Laravel поддерживает несколько широковещательных драйверов из коробки: Pusher, Redis и драйвер log для локальной разработки и отладки. Пример конфигурации включен для каждого из этих драйверов.
Требования к широковещательным событиям
Следующие зависимости будут необходимы:
- Pusher: pusher/pusher-php-server ~2.0
- Redis: predis/predis ~1.0
Требования к очереди
Перед использованием широковещательных событий также вам будет необходимо сконфигурировать и запустить слушателя очереди. Все широковещательные события используют очереди, чтобы не уменьшать время отклика вашего приложения.
Помечаем широковещательные события
Чтобы проинформировать Laravel о том, что заданное событие должно быть широковещательным, реализуйте интерфейс Illuminate\Contracts\Broadcasting\ShouldBroadcast в классе события. Интерфейс ShouldBroadcast требует реализации одного метода: broadcastOn
()
. Метод broadcastOn
()
должен возвращать массив «channel» имён, для которого событие должно быть широковещательным:
<?php namespace App\Events; use App\User; use App\Events\Event; use Illuminate\Queue\SerializesModels; use Illuminate\Contracts\Broadcasting\ShouldBroadcast; class ServerCreated extends Event implements ShouldBroadcast { use SerializesModels; public $user; /** * Создание нового экземпляра события. * * @return void */ public function __construct(User $user) { $this->user = $user; } /** * Получение каналов, для которых событие должно быть широковещательным. * * @return array */ public function broadcastOn() { return ['user.'.$this->user->id]; } }
Теперь вам нужно только запустить событие, как вы это обычно делали. Как только событие было запущено, обработчик очереди автоматически передаст широковещательное событие по вашему указанному широковещательному драйверу.
+ 5.1 5.0
добавлено в 5.1 (19.06.2016) 5.0 (08.02.2016)
Переписываем имена широковещательных событий
По умолчанию широковещательное имя события будет полностью определенным именем класса события. Используя класс в качестве примера выше, широковещательное событие было бы App\Events\ServerCreated. Вы можете определить имя широковещательного события, как вам будет удобнее, используя метод broadcastAs
()
:
/** * Получить имя широковещательного события. * * @return string */ public function broadcastAs() { return 'app.server-created'; }
Широковещательные данные
Когда событие широковещательное, все его public свойства автоматически сериализированы и передаются вместе с событием, что позволяет вам получить доступ к любым из его общедоступных данных из JavaScript приложения. Например, если у вашего события есть единственное общедоступное свойство $user, которое содержит Eloquent модель, широковещательные данные будут выглядеть так:
{
"user": {
"id": 1,
"name": "Jonathan Banks"
...
}
}
Однако, если вы хотите иметь еще более тщательный контроль над своими широковещательными данными, вы можете добавить к своему событию метод broadcastWith
()
. Этот метод должен возвращать массив данных, которые вы хотите передать с событием:
/** * Получить данные для передачи. * * @return array */ public function broadcastWith() { return ['user' => $this->user->id]; }
+ 5.2
добавлено в 5.2 (08.12.2016)
Настройка широковещательных событий
Настройка имени события
По умолчанию имя широковещательного события — это полное имя класса этого события. Например, если имя класса App\Events\ServerCreated, то событие будет App\Events\ServerCreated. Имя можно изменить, определив метод broadcastAs
()
в классе события:
/** * Получение имени широковещательного события. * * @return string */ public function broadcastAs() { return 'app.server-created'; }
Настройка очереди
По умолчанию каждое широковещательное событие помещается в очередь по умолчанию для подключения по умолчанию в вашем файле настроек queue.php. Можно изменить очередь для вещания событий, добавив метод onQueue
()
в класс события. Этот метод должен возвращать имя нужной очереди:
/** * Задание имени очереди для размещения событий. * * @return string */ public function onQueue() { return 'your-queue-name'; }
Использование широковещательных событий
Pusher
Вы можете удобно использовать широковещательную передачу событий, используя драйвер Pusher, используя Pusher SDK JavaScript. Например, давайте используем событие App\Events\ServerCreated из наших предыдущих примеров:
this.pusher = new Pusher('pusher-key'); this.pusherChannel = this.pusher.subscribe('user.' + USER_ID); this.pusherChannel.bind('App\\Events\\ServerCreated', function(message) { console.log(message.user); });
Redis
Если вы будете использовать Redis, то вы должны будете написать свой собственный Redis потребитель типа издатель-подписчик, чтобы получать сообщения и широковещательно передавать их, используя websocket технологию на ваш выбор. Например, вы можете воспользоваться популярной библиотекой Socket.io, которая написана на Node.
Используя библиотеки Node socket.io и ioredis, вы можете быстро написать broadcaster событий, чтобы публиковать все события, которые широковещаются вашим приложением Laravel:
var app = require('http').createServer(handler); var io = require('socket.io')(app); var Redis = require('ioredis'); var redis = new Redis(); app.listen(6001, function() { console.log('Server is running!'); }); function handler(req, res) { res.writeHead(200); res.end(''); } io.on('connection', function(socket) { // }); redis.psubscribe('*', function(err, count) { // }); redis.on('pmessage', function(subscribed, channel, message) { message = JSON.parse(message); io.emit(channel + ':' + message.event, message.data); });
Подписчики событий
Подписчики событий — это классы, которые могут подписаться на множество событий из самого класса, что позволяет вам определить несколько обработчиков событий в одном классе. Подписчики должны определить метод subscribe
()
, в который будет передан экземпляр диспетчера события:
<?php namespace App\Listeners; class UserEventListener { /** * Обработка события входа пользователя в систему. */ public function onUserLogin($event) {} /** * Обработка события выхода пользователя из системы. */ public function onUserLogout($event) {} /** * Регистрация слушателей для подписки. * * @param Illuminate\Events\Dispatcher $events */ public function subscribe($events) { $events->listen( 'App\Events\UserLoggedIn', 'App\Listeners\UserEventListener@onUserLogin' ); $events->listen( 'App\Events\UserLoggedOut', 'App\Listeners\UserEventListener@onUserLogout' ); } }
Регистрация слушателей для подписки
Когда подписчик определён, он может быть зарегистрирован в диспетчере события. Вы можете зарегистрировать подписчиков, используя свойство $subscribe в EventServiceProvider. Например, давайте добавим UserEventListener.
<?php namespace App\Providers; use Illuminate\Contracts\Events\Dispatcher as DispatcherContract; use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider; class EventServiceProvider extends ServiceProvider { /** * Слушатель события для приложения. * * @var array */ protected $listen = [ // ]; /** * Классы подписчиков для регистрации. * * @var array */ protected $subscribe = [ 'App\Listeners\UserEventListener', ]; }
+ 5.1 5.0
добавлено в 5.1 (19.06.2016) 5.0 (08.02.2016)
События фреймворка
Laravel предоставляет множество «базовых» событий для действий, выполняемых фреймворком. Вы можете подписаться на них таким же образом, как вы подписываетесь на свои собственные события:
Событие |
Параметр(ы) |
artisan.start |
$application |
auth.attempt |
$credentials, $remember, $login |
auth.login |
$user, $remember |
auth.logout |
$user |
cache.missed |
$key |
cache.hit |
$key, $value |
cache.write |
$key, $value, $minutes |
cache.delete |
$key |
connection.{name}.beganTransaction |
$connection |
connection.{name}.committed |
$connection |
connection.{name}.rollingBack |
$connection |
illuminate.query |
$query, $bindings, $time, $connectionName |
illuminate.queue.after |
$connection, $job, $data |
illuminate.queue.failed |
$connection, $job, $data |
illuminate.queue.stopping |
null |
mailer.sending |
$message |
router.matched |
$route, $request |
{view name} |
$view |
{view name} |
$view |