Оригинал: http://www.sitepoint.com/real-time-apps-laravel-5-1-event-broadcasting/
Версия Laravel 5.1 включает в себя функционал, называемый вещанием событий, который с легкостью позволяет создавать приложения реального времени (real-time) на PHP. Этот функционал позволяет приложению посылать события на различные «облачные» PubSub-сервисы, наподобие Pusher‘а, или в Redis.
В этой статье мы разберем простое TODO-приложение и превратим его в real-time приложение, используя вещание событий в Laravel.
Скачайте и установите приложение
Самый простой способ начать, это развернуть Homestead. Если же вы не хотите использовать Homestead, у вас в системе должны быть установлены git и composer.
Мы положим начальный код в директорию todo-app путем клонирования репозитория из git’а.
1 |
git clone https://github.com/cwt137/l51-todo-app todo-app |
Как только клонирование завершится, перейдите в директорию todo-app . Нам необходимо установить все зависимости приложения, выполнив следующую команду (в зависимости от того, как в вашей системе установлен Composer, команда может быть немного другой):
1 |
composer install |
После установки зависимостей необходимо проинициализировать базу данных.
1 |
php artisan migrate |
Тестируем приложение (пока еще не real-time)
Это приложение уже функционирует, но оно еще не является приложением реального времени. Откройте домашнюю страницу в двух браузерах и расположите их рядом друг с другом, чтобы вы могли сравнивать их содержимое. Если у вас нет двух разных браузеров, можно использовать два разных окна одного браузера.
Поиграйтесь с todo-списком в первом окне. Затем сделайте что-нибудь во втором окне. Вы заметите, что содержимое в другом окне (не в том, в котором вы в данные мамомент манипулируете списком) не обновляется самостоятельно, а только при ручном обновлении страницы. Это потому, что в приложении еще нет real-time функционала. Давайте добавим его.
Добавление real-time возможностей в приложение
Добавление real-time возможностей позволит обоим окнам обновлять содержимое без необходимости нажимать кнопку «Обновить».
В данном примере мы определим три класса-события, которые будут срабатывать в разные моменты времени в нашем приложении. Первое событие — это ItemCreated , которое будет срабатывать каждый раз, когда будет создан новый элемент. Второе событие — ItemUpdated , которое будет срабатывать при изменении элемента (завершен или не завершен). И, наконец, последнее — ItemDeleted , которое будет срабатывает при удалении элемента из todo-списка.
Широковещательные события
Для работы с широковещательными событиями, мы создадим обычное событие Laravel, но при этом будем реализовывать интерфейс ShouldBroadcast . Когда Laravel видит, что событие реализует этот интерфейс, он понимает, что это — широковещательное событие. Этот интерфейс требует от нас реализации метода broadcastOn . Он должен возвращать массив строк-названий каналов, на которые следует вещать данное событие.
Чтобы создать нужные нам классы-события, нужно выполнить несколько команд artisan’а:
1 2 3 |
php artisan make:event ItemCreated php artisan make:event ItemUpdated php artisan make:event ItemDeleted |
Откройте файл app/Events/ItemCreated.php и замените его содержимое следующим кодом:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
<?php namespace App\Events; use App\Item; use App\Events\Event; use Illuminate\Queue\SerializesModels; use Illuminate\Contracts\Broadcasting\ShouldBroadcast; class ItemCreated extends Event implements ShouldBroadcast { use SerializesModels; public $id; /** * Create a new event instance. * * @param Item $item * @return void */ public function __construct(Item $item) { $this->id = $item->id; } /** * Get the channels the event should be broadcast on. * * @return array */ public function broadcastOn() { return ['itemAction']; } } |
Система событий Laravel впоследствии сериализует этот объект и будет вещать его в облачный сервис на канал itemAction . Оставшаяся пара событий похожа на первое.
Откройте файл app/Events/ItemUpdated.php и замените его содержимое следующим кодом:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
<?php namespace App\Events; use App\Item; use App\Events\Event; use Illuminate\Queue\SerializesModels; use Illuminate\Contracts\Broadcasting\ShouldBroadcast; class ItemUpdated extends Event implements ShouldBroadcast { use SerializesModels; public $id; public $isCompleted; /** * Create a new event instance. * * @param Item $item * @return void */ public function __construct(Item $item) { $this->id = $item->id; $this->isCompleted = (bool) $item->isCompleted; } /** * Get the channels the event should be broadcast on. * * @return array */ public function broadcastOn() { return ['itemAction']; } } |
Откройте файл app/Events/ItemDeleted.php и замените его содержимое следующим кодом:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
<?php namespace App\Events; use App\Item; use App\Events\Event; use Illuminate\Queue\SerializesModels; use Illuminate\Contracts\Broadcasting\ShouldBroadcast; class ItemDeleted extends Event implements ShouldBroadcast { use SerializesModels; public $id; /** * Create a new event instance. * * @param Item $item * @return void */ public function __construct(Item $item) { $this->id = $item->id; } /** * Get the channels the event should be broadcast on. * * @return array */ public function broadcastOn() { return ['itemAction']; } } |
События базы данных
Существует несколько мест, в которых мы могли бы «выбрасывать» наши события. Мы могли бы делать это внутри контроллера, или же внутри событий базы данных. В этом примере мы воспользуемся событиями базы данных, поскольку лично мне это кажется более естественным и не захламляет контроллер лишним кодом. Также, выбрасывание событий из слоя базы данных делает возможным возбуждение этих событий из, например, скрипта для командной строки, или же по cron-скрипту.
Библиотека для работы с БД Eloquent вызывает события каждый раз, когда модель создается, сохранятся после обновления, или удаляется. Мы будем прослушивать эти события, чтобы возбуждать свои собственные широковещательные события. Это можно сделать внутри сервис-провайдера.
Откройте файл app/Providers/AppServiceProvider.php и замените его содержимое следующим кодом:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
<?php namespace App\Providers; use Event; use App\Item; use App\Events\ItemCreated; use App\Events\ItemUpdated; use App\Events\ItemDeleted; use Illuminate\Support\ServiceProvider; class AppServiceProvider extends ServiceProvider { /** * Bootstrap any application services. * * @return void */ public function boot() { Item::created(function ($item) { Event::fire(new ItemCreated($item)); }); Item::updated(function ($item) { Event::fire(new ItemUpdated($item)); }); Item::deleted(function ($item) { Event::fire(new ItemDeleted($item)); }); } /** * Register any application services. * * @return void */ public function register() { // } } |
Pusher
В данном примере мы воспользуется облачным сервисом Pusher. Поддержка этого сервиса встроена в Laravel и его легче всего настроить.
Регистрация
Мы должны зарегистрировать учетную запись, чтобы получить набор идентификационных данных. После регистрации на сайте Pusher’а, перейдите в раздел администрирования и создайте новое приложение todo-app . Обратите внимание на app_id , key и secret . Они понадобятся нам позже.
Серверная библиотека для работы с Pusher’ом
Чтобы наше приложение смогло начать использовать Pusher, необходимо добавить соответствующую библиотеку. Это делается при помощи composer’а:
1 |
composer require 'pusher/pusher-php-server:2.2.1' |
JavaScript
Мы также добавим немного JavaScript-кода на нашу странице. Откройте resources/views/index.blade.php (шаблон домашней страницы), и разместите следующий код прямо перед закрывающим тэгом body :
1 2 3 4 5 |
<script src="//js.pusher.com/2.2/pusher.min.js"></script> <script> var pusher = new Pusher("{{ env(PUSHER_KEY) }}"); </script> <script src="js/pusher.js"></script> |
Код, приведенный выше, загружает клиентскую JavaScript библиотеку для работы с Pusher’ом, создает экземпляр Pusher, передавая наш ключ (key) в конструктор, а также загружает специфичную логику нашего приложения.
В начальной версии приложения элементы списка добавлялись и удалялись на основе событий, происходящих на странице (отправка формы, щелчок по иконке удаления, и т.д.). Поэтому мы открыли два окна, и оба не обновлялись автоматически. Одно окно не может видеть, что происходит в другом. Чтобы сделать приложение «живым» (real-time), мы будем добавлять и удалять элементы на основе событий из Pusher’а. Но сначала откройте фалй public/js/app.js и закомментируйте все вызовы методов addItem() и removeItem() . Но будьте внимательны — не удалите сами определения этих функций.
Создайте файл public/js/pusher.js и вставьте в него следующий код:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
( function( $, pusher, addItem, removeItem ) { var itemActionChannel = pusher.subscribe( 'itemAction' ); itemActionChannel.bind( "App\\Events\\ItemCreated", function( data ) { addItem( data.id, false ); } ); itemActionChannel.bind( "App\\Events\\ItemUpdated", function( data ) { removeItem( data.id ); addItem( data.id, data.isCompleted ); } ); itemActionChannel.bind( "App\\Events\\ItemDeleted", function( data ) { removeItem( data.id ); } ); } )( jQuery, pusher, addItem, removeItem); |
Приложение подписывается на канал itemAction . По умолчанию, Laravel использует полное имя класса-события в качестве имени события для Pusher’а. JavaScript код, приведенный выше, прослушивает три события: App\Events\ItemCreated , App\Events\ItemUpdated и App\Events\ItemDeleted . Имеются также обратные вызовы функций, которые обрабатывают эти события. Таким образом, теперь элементы добавляются или удаляются на основе событий от Pusher’а.
Тестируем наше приложение
Чтобы протестировать приложение, вам нужно будет установить ключи для Pusher’а. По умолчанию, Laravel ищет их среди переменных окружения. Откройте файл .env и разместите внизу следующие строки:
1 2 3 |
PUSHER_KEY=YOUR_PUSHER_KEY PUSHER_SECRET=YOUR_PUSHER_SECRET PUSHER_APP_ID=YOUR_PUSHER_APP_ID |
Таким образом вы установите переменные. Здесь YOUR_PUSHER_KEY , YOUR_PUSHER_SECRET и YOUR_PUSHER_APP_ID — это, соответственно, ваши key , secret и app_id , полученные при регистрации приложения в Pusher’е.
Снова откройте два окна браузера и поиграйтесь со списком задач в одном из них. Вы увидите, что список в другом окне обновляется в соответствии с вашими действиями, без необходимости вручную обновлять страницу.
Напоследок
Хоть об этом и не было сказано в этой статье, используемый фреймоврком механизм является расширяемым, и если Laravel не поддерживает конкретно ваше real-time решение, возможно уже существует готовый пакет с подходящим широковещательным драйвером для него. Или же вы можете написать свой собственный драйвер.
С новым механизмом вещания событий в Laravel 5.1 PHP-разработчикам стало проще разрабатывать real-time приложения. Этот механизм открывает многие возможности, ранее доступные лишь приложениям, написанным для других платформ вроде Node.js.