{{TOC}} {{DOCVER 5.3=c06d6a2352ed8c767633aab9c20f2bf7d880c967 28.01.2017 5:00:51, 5.2=6b0b057ae6de3c88cb29188459e38383c622ec23 8.12.2016 23:00:15, 5.1=cdc24ba7426c5b11eb4d050706bd78c3ea4913cc 19.06.2016 20:08:01, 5.0=5d10040a981deee82c0fde0e8e5d2ffc49eaaecb 8.02.2016 18:09:11}} == Введение == События в Laravel представлены реализацией паттерна Observer, что позволяет вам подписываться и прослушивать различные события, возникающие в вашем приложения. Как правило, классы событий находятся в папке %%(t)app/Events%%, а классы обработчиков событий - в %%(t)app/Listeners%%. Если у вас нет этих папок, не переживайте, они будут созданы при создании событий и слушателей с помощью Artisan-команд. События - отличный способ для разделения различных аспектов вашего приложения, поскольку одно событие может иметь несколько слушателей, независящих друг от друга. Например, вы можете отправлять пользователю Slack-уведомление каждый раз, когда заказ доставлен. Вместо привязки кода обработки заказа к коду Slack-уведомления вы можете просто создать событие %%(t)OrderShipped%%, которое сможет получить слушатель и преобразовать в Slack-уведомление. == Регистрация событий и слушателей == Сервис-провайдер %%(t)EventServiceProvider%%, включённый в ваше Laravel приложение, предоставляет удобное место для регистрации всех слушателей событий. Свойство %%(t)listen%% содержит массив всех событий (ключей) и их слушателей (значения). Конечно, вы можете добавить столько событий в этот массив, сколько требуется вашему приложению. Например, давайте добавим событие %%(t)OrderShipped%%: %% /** * Слушатель события в вашем приложении. * * @var array */ protected $listen = [ 'App\Events\OrderShipped' => [ 'App\Listeners\SendShipmentNotification', ], ]; %% === Генерация классов событий и слушателей === Конечно, вручную создавать файлы для каждого события и слушателя затруднительно. Вместо этого добавьте слушателей и события в ваш %%(t)EventServiceProvider%% и используйте команду %%(sh)event:generate%%. Эта команда сгенерирует все события и слушателей, которые перечислены в вашем %%(t)EventServiceProvider%%. Конечно, уже существующие события и слушатели останутся нетронутыми: %%(sh) php artisan event:generate %% === Регистрация событий вручную === Как правило, события должны регистрироваться через массив %%(t)$listen%% в %%(t)EventServiceProvider%%. %%(DOCNEW 5.3=c06d6a2352ed8c767633aab9c20f2bf7d880c967 28.01.2017 5:00:51) Однако, также вы можете вручную регистрировать события на основе замыканий в методе %%boot()%% вашего %%(t)EventServiceProvider%%: ~%% /** * Регистрация своих событий в приложении. * * @return void */ public function boot() { parent::boot(); Event::listen('event.name', function ($foo, $bar) { // }); } ~%% %% %%(DOCNEW 5.2=6b0b057ae6de3c88cb29188459e38383c622ec23 8.12.2016 23:00:15, 5.1=cdc24ba7426c5b11eb4d050706bd78c3ea4913cc 19.06.2016 20:08:01, 5.0=5d10040a981deee82c0fde0e8e5d2ffc49eaaecb 8.02.2016 18:09:11) Однако, также вы можете регистрировать события вручную с обработчиком событий, используя либо фасад %%(t)Event%%, либо реализацию контракта %%(t)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) { // }); } ~%% %% **Слушатели событий по маске** Вы даже можете регистрировать слушателей, используя символ %%(t)*%% как маску, что позволит вам поймать несколько событий для одного слушателя. Такой метод вернёт весь массив данных событий одним параметром: %% Event::listen('event.*', function (array $data) { // }); %% == Определение событий == Класс события - это просто контейнер данных, содержащий информацию, которая относится к событию. %%(DOCNEW 5.3=c06d6a2352ed8c767633aab9c20f2bf7d880c967 28.01.2017 5:00:51) Например, предположим, что наше сгенерированное событие %%(t)OrderShipped%% принимает объект ((/docs/v5/eloquent Eloquent ORM)): ~%% order = $order; } } ~%% Как видите, этот класс события не содержит никакой логики. Это просто контейнер для объекта %%(t)Order%%. Типаж %%(t)SerializesModels%%, используемый событием, корректно сериализирует любые Eloquent модели, если объект события будет сериализирован php-функцией %%serialize()%%. %% %%(DOCNEW 5.2=6b0b057ae6de3c88cb29188459e38383c622ec23 8.12.2016 23:00:15, 5.1=cdc24ba7426c5b11eb4d050706bd78c3ea4913cc 19.06.2016 20:08:01, 5.0=5d10040a981deee82c0fde0e8e5d2ffc49eaaecb 8.02.2016 18:09:11) Например, предположим, что наше сгенерированное событие %%(t)PodcastWasPurchased%% принимает объект ((/docs/v5/eloquent Eloquent ORM)): ~%% podcast = $podcast; } } ~%% Как видите, этот класс события не содержит никакой логики. Это просто контейнер для объекта %%(t)Podcast%%. Типаж %%(t)SerializesModels%%, используемый событием, корректно сериализирует любые Eloquent модели, если объект события будет сериализирован php-функцией %%serialize()%%. %% == Определение слушателей == Теперь давайте взглянем на слушателя для нашего примера события. Слушатели событий принимают экземпляр события в свой метод %%handle()%%. Команда %%(sh)event:generate%% автоматически импортирует класс события и указывает тип события в метод %%handle()%%. В методе %%handle()%% вы можете выполнять любые действия, необходимые для ответа на событие. %%(DOCNEW 5.3=c06d6a2352ed8c767633aab9c20f2bf7d880c967 28.01.2017 5:00:51) ~%% order... } } ~%% .(alert) Ваши слушатели события могут также указывать тип любых зависимостей, которые необходимы для их конструкторов. Все слушатели события доступны через ((/docs/v5/container сервис-контейнер)) Laravel, поэтому зависимости будут инъецированы автоматически. %% %%(DOCNEW 5.2=6b0b057ae6de3c88cb29188459e38383c622ec23 8.12.2016 23:00:15, 5.1=cdc24ba7426c5b11eb4d050706bd78c3ea4913cc 19.06.2016 20:08:01, 5.0=5d10040a981deee82c0fde0e8e5d2ffc49eaaecb 8.02.2016 18:09:11) ~%% podcast... } } ~%% Ваши слушатели события могут также указывать тип любых зависимостей, которые необходимы для их конструкторов. Все слушатели события доступны через ((/docs/v5/container сервис-контейнер)) Laravel, поэтому зависимости будут инъецированы автоматически: ~%% use Illuminate\Contracts\Mail\Mailer; public function __construct(Mailer $mailer) { $this->mailer = $mailer; } ~%% %% **Остановка распространения события** Иногда, вам необходимо остановить распространение события для других слушателей. Вы можете сделать это, возвратив %%(t)false%% из метода %%handle()%% вашего слушателя. == Слушатели события в очереди == Поместить слушателя в очередь может быть полезно, если ваш слушатель будет выполнять медленную задачу, например, отправку e-mail или выполнение HTTP-запроса. Прежде чем помещать слушателей в очередь, не забудьте ((//docs/v5/queues настроить вашу очередь)) и запустить слушателя очереди на вашем сервере или в локальной среде разработки. Чтобы указать, что слушатель должен быть поставлен в очередь, добавьте интерфейс %%(t)ShouldQueue%% в класс слушателя. В слушателях, сгенерированных Artisan-командой %%(sh)event:generate%%, уже импортирован этот интерфейс в текущее пространство имен. Так что вы можете сразу использовать его: %% release(30); } } } %% == Запуск событий == %%(DOCNEW 5.3=c06d6a2352ed8c767633aab9c20f2bf7d880c967 28.01.2017 5:00:51) Чтобы запустить событие, вы можете передать экземпляр события во вспомогательный метод %%event()%%. Этот метод распространит событие для всех его зарегистрированных слушателей. Поскольку метод %%event()%% доступен глобально, вы можете вызвать его из любого места вашего приложения: ~%% user = $user; } /** * Получение каналов, для которых событие должно быть широковещательным. * * @return array */ public function broadcastOn() { return ['user.'.$this->user->id]; } } ~%% Теперь вам нужно только ((/docs/v5/events#запуск запустить событие)), как вы это обычно делали. Как только событие было запущено, ((/docs/v5/queues обработчик очереди)) автоматически передаст широковещательное событие по вашему указанному широковещательному драйверу. %% %%(DOCNEW 5.1=cdc24ba7426c5b11eb4d050706bd78c3ea4913cc 19.06.2016 20:08:01, 5.0=5d10040a981deee82c0fde0e8e5d2ffc49eaaecb 8.02.2016 18:09:11) **Переписываем имена широковещательных событий** По умолчанию широковещательное имя события будет полностью определенным именем класса события. Используя класс в качестве примера выше, широковещательное событие было бы %%(t)App\Events\ServerCreated%%. Вы можете определить имя широковещательного события, как вам будет удобнее, используя метод %%broadcastAs()%%: ~%% /** * Получить имя широковещательного события. * * @return string */ public function broadcastAs() { return 'app.server-created'; } ~%% %% %%(DOCNEW 5.2=6b0b057ae6de3c88cb29188459e38383c622ec23 8.12.2016 23:00:15, 5.1=cdc24ba7426c5b11eb4d050706bd78c3ea4913cc 19.06.2016 20:08:01, 5.0=5d10040a981deee82c0fde0e8e5d2ffc49eaaecb 8.02.2016 18:09:11) === Широковещательные данные === Когда событие широковещательное, все его %%(t)public%% свойства автоматически сериализированы и передаются вместе с событием, что позволяет вам получить доступ к любым из его общедоступных данных из JavaScript приложения. Например, если у вашего события есть единственное общедоступное свойство %%(t)$user%%, которое содержит Eloquent модель, широковещательные данные будут выглядеть так: %%(json) { "user": { "id": 1, "name": "Jonathan Banks" ... } } ~%% Однако, если вы хотите иметь еще более тщательный контроль над своими широковещательными данными, вы можете добавить к своему событию метод %%broadcastWith()%%. Этот метод должен возвращать массив данных, которые вы хотите передать с событием: ~%% /** * Получить данные для передачи. * * @return array */ public function broadcastWith() { return ['user' => $this->user->id]; } ~%% %% %%(DOCNEW 5.2=6b0b057ae6de3c88cb29188459e38383c622ec23 8.12.2016 23:00:15) === Настройка широковещательных событий === **Настройка имени события** По умолчанию имя широковещательного события - это полное имя класса этого события. Например, если имя класса %%(t)App\Events\ServerCreated%%, то событие будет %%(t)App\Events\ServerCreated%%. Имя можно изменить, определив метод %%broadcastAs()%% в классе события: ~%% /** * Получение имени широковещательного события. * * @return string */ public function broadcastAs() { return 'app.server-created'; } ~%% **Настройка очереди** По умолчанию каждое широковещательное событие помещается в очередь по умолчанию для подключения по умолчанию в вашем файле настроек %%(t)queue.php%%. Можно изменить очередь для вещания событий, добавив метод %%onQueue()%% в класс события. Этот метод должен возвращать имя нужной очереди: ~%% /** * Задание имени очереди для размещения событий. * * @return string */ public function onQueue() { return 'your-queue-name'; } ~%% %% %%(DOCNEW 5.2=6b0b057ae6de3c88cb29188459e38383c622ec23 8.12.2016 23:00:15, 5.1=cdc24ba7426c5b11eb4d050706bd78c3ea4913cc 19.06.2016 20:08:01, 5.0=5d10040a981deee82c0fde0e8e5d2ffc49eaaecb 8.02.2016 18:09:11) === Использование широковещательных событий === **Pusher** Вы можете удобно использовать широковещательную передачу событий, используя драйвер ((https://pusher.com/ Pusher)), используя Pusher SDK JavaScript. Например, давайте используем событие %%(t)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 технологию на ваш выбор. Например, вы можете воспользоваться популярной библиотекой ((http://socket.io/ Socket.io)), которая написана на Node. Используя библиотеки Node %%(t)socket.io%% и %%(t)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()%%, в который будет передан экземпляр диспетчера события. Вы можете вызвать метод %%listen()%% на данном диспетчере для регистрации слушателей события: %% listen( 'Illuminate\Auth\Events\Login', 'App\Listeners\UserEventSubscriber@onUserLogin' ); $events->listen( 'Illuminate\Auth\Events\Logout', 'App\Listeners\UserEventSubscriber@onUserLogout' ); } } %% === Регистрация подписчика события === После написания подписчика, вы можете зарегистрировать его в диспетчере события. Вы можете зарегистрировать подписчиков, используя свойство %%(t)$subscribe%% в %%(t)EventServiceProvider%%. Например, давайте добавим %%(t)UserEventSubscriber%%. %% Событие Параметр(ы) 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.stoppingnull mailer.sending$message router.matched$route, $request {view name}$view {view name}$view ~%% %%