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 |
