() translation by (you can also view the original English article)
Сегодня мы рассмотрим концепцию вещания в веб-среде Laravel. Она позволяет отправлять уведомления стороне клиента, когда что-то происходит на стороне сервера. В этой статье мы собираемся использовать стороннюю библиотеку Pusher для отправки уведомлений на стороне клиента.
Если вы когда-либо хотели отправлять уведомления с сервера клиенту, когда что-то происходит на сервере в Laravel, вам определенно нужна фича broadcasting.
Например, предположим, что вы внедрили приложение обмена сообщениями, которое позволяет пользователям вашей системы отправлять сообщения друг другу. Теперь, когда пользователь A отправляет сообщение пользователю B, вы хотите уведомить пользователя B в режиме реального времени. Вы можете отобразить всплывающее окно или окно предупреждения, которое информирует пользователя B о новом сообщении!
Это идеальный use-case, чтобы рассмотреть концепцию трансляции в Laravel, и это то, что мы будем реализовывать в этой статье.
Если вам интересно, как сервер может отправлять уведомления клиенту, для его выполнения используются сокеты под капотом. Давайте рассмотрим основной поток сокетов, прежде чем погрузиться в реальную реализацию.
- Во-первых, вам нужен сервер, поддерживающий протокол веб-сокетов и позволяющий клиенту устанавливать соединение с веб-сокетом.
- Вы можете реализовать свой собственный сервер или использовать стороннюю службу, такую как Pusher. Мы предпочтем последнее в этой статье.
- Клиент инициирует подключение веб-сокета к серверу веб-сокетов и получает уникальный идентификатор при успешном соединении.
- Как только соединение будет успешным, клиент подписывается на определенные каналы, по которым он хочет получать события.
- Наконец, по подписанному каналу клиент регистрирует события, которые он хотел бы прослушать.
- Теперь на стороне сервера, когда происходит определенное событие, мы сообщаем серверу веб-сокетов, предоставляя ему имя канала и имя события.
- И, наконец, сервер веб-сокетов передает это событие зарегистрированным клиентам на этом конкретном канале.
Не беспокойтесь, если это выглядит слишком много за один раз; вы получите это, когда мы перейдем к этой статье.
Затем давайте посмотрим на файл конфигурации broadcasting по умолчанию в config/broadcasting.php
.
1 |
<?php
|
2 |
|
3 |
return [ |
4 |
|
5 |
/*
|
6 |
|--------------------------------------------------------------------------
|
7 |
| Default Broadcaster
|
8 |
|--------------------------------------------------------------------------
|
9 |
|
|
10 |
| This option controls the default broadcaster that will be used by the
|
11 |
| framework when an event needs to be broadcast. You may set this to
|
12 |
| any of the connections defined in the "connections" array below.
|
13 |
|
|
14 |
| Supported: "pusher", "redis", "log", "null"
|
15 |
|
|
16 |
*/
|
17 |
|
18 |
'default' => env('BROADCAST_DRIVER', 'log'), |
19 |
|
20 |
/*
|
21 |
|--------------------------------------------------------------------------
|
22 |
| Broadcast Connections
|
23 |
|--------------------------------------------------------------------------
|
24 |
|
|
25 |
| Here you may define all of the broadcast connections that will be used
|
26 |
| to broadcast events to other systems or over websockets. Samples of
|
27 |
| each available type of connection are provided inside this array.
|
28 |
|
|
29 |
*/
|
30 |
|
31 |
'connections' => [ |
32 |
|
33 |
'pusher' => [ |
34 |
'driver' => 'pusher', |
35 |
'key' => env('PUSHER_APP_KEY'), |
36 |
'secret' => env('PUSHER_APP_SECRET'), |
37 |
'app_id' => env('PUSHER_APP_ID'), |
38 |
],
|
39 |
|
40 |
'redis' => [ |
41 |
'driver' => 'redis', |
42 |
'connection' => 'default', |
43 |
],
|
44 |
|
45 |
'log' => [ |
46 |
'driver' => 'log', |
47 |
],
|
48 |
|
49 |
'null' => [ |
50 |
'driver' => 'null', |
51 |
],
|
52 |
|
53 |
],
|
54 |
|
55 |
];
|
По умолчанию Laravel поддерживает несколько адаптеров в своем ядре.
В этой статье мы собираемся использовать адаптер Pusher
. Для целей отладки вы также можете использовать адаптер log. Конечно, если вы используете адаптер log
, клиент не получит никаких уведомлений о событиях, и он будет зарегистрирован только в файле laravel.log
.
Из следующего раздела дальше мы сразу же погрузимся в фактическую реализацию вышеупомянутого варианта использования.
Настройка предварительных условий
В вещании существуют разные типы каналов - публичные, частные и присутствующие. Если вы хотите публиковать свои мероприятия публично, то вы должны использовать общедоступный канал. И наоборот, приватный канал используется, когда вы хотите ограничить уведомления о событиях некоторыми привтаными каналами.
В нашем случае мы хотим уведомить пользователей, когда они получат новое сообщение. И чтобы иметь право получать уведомления о трансляции, пользователь должен войти в систему. Таким образом, нам нужно будет использовать приватный канал в нашем случае.
Основная функция проверки подлинности
Во-первых, вам нужно включить стандартную систему проверки подлинности Laravel, чтобы такие функции, как регистрация, вход в систему и т.п., работали из коробки. Если вы не знаете, как это сделать, официальная документация дает быстрое представление об этом.
Pusher SDK - установка и настройка
Поскольку мы собираемся использовать сторонний сервис Pusher
в качестве нашего сервера веб-сокетов, вам необходимо создать учетную запись и убедиться, что у вас есть необходимые учетные данные API. Если у вас возникли проблемы с его созданием, не стесняйтесь спрашивать меня в разделе комментариев.
Затем нам нужно установить Pusher PHP SDK, чтобы наше приложение Laravel могло отправлять уведомления на сервер веб-сокетов Pusher.
В корне своего приложения Laravel запустите следующую команду, чтобы установить его в качестве пакета composer.
1 |
$composer require pusher/pusher-php-server "~3.0" |
Теперь давайте изменим файл конфигурации трансляции, чтобы включить адаптер Pusher в качестве нашего драйвера по умолчанию.
1 |
<?php
|
2 |
|
3 |
return [ |
4 |
|
5 |
/*
|
6 |
|--------------------------------------------------------------------------
|
7 |
| Default Broadcaster
|
8 |
|--------------------------------------------------------------------------
|
9 |
|
|
10 |
| This option controls the default broadcaster that will be used by the
|
11 |
| framework when an event needs to be broadcast. You may set this to
|
12 |
| any of the connections defined in the "connections" array below.
|
13 |
|
|
14 |
| Supported: "pusher", "redis", "log", "null"
|
15 |
|
|
16 |
*/
|
17 |
|
18 |
'default' => env('BROADCAST_DRIVER', 'pusher'), |
19 |
|
20 |
/*
|
21 |
|--------------------------------------------------------------------------
|
22 |
| Broadcast Connections
|
23 |
|--------------------------------------------------------------------------
|
24 |
|
|
25 |
| Here you may define all of the broadcast connections that will be used
|
26 |
| to broadcast events to other systems or over websockets. Samples of
|
27 |
| each available type of connection are provided inside this array.
|
28 |
|
|
29 |
*/
|
30 |
|
31 |
'connections' => [ |
32 |
|
33 |
'pusher' => [ |
34 |
'driver' => 'pusher', |
35 |
'key' => env('PUSHER_APP_KEY'), |
36 |
'secret' => env('PUSHER_APP_SECRET'), |
37 |
'app_id' => env('PUSHER_APP_ID'), |
38 |
'options' => [ |
39 |
'cluster' => 'ap2', |
40 |
'encrypted' => true |
41 |
],
|
42 |
],
|
43 |
|
44 |
'redis' => [ |
45 |
'driver' => 'redis', |
46 |
'connection' => 'default', |
47 |
],
|
48 |
|
49 |
'log' => [ |
50 |
'driver' => 'log', |
51 |
],
|
52 |
|
53 |
'null' => [ |
54 |
'driver' => 'null', |
55 |
],
|
56 |
|
57 |
],
|
58 |
|
59 |
];
|
Как вы можете видеть, мы изменили драйвер передачи по умолчанию на Pusher. Мы также добавили параметры конфигурации cluster и encrypted , которые вы должны были получить из учетной записи Pusher, в первую очередь.
Кроме того, он извлекает значения из переменных среды. Поэтому давайте проверим, правильно ли заданы следующие переменные в файле .env
.
1 |
BROADCAST_DRIVER=pusher |
2 |
|
3 |
PUSHER_APP_ID={YOUR_APP_ID} |
4 |
PUSHER_APP_KEY={YOUR_APP_KEY} |
5 |
PUSHER_APP_SECRET={YOUR_APP_SECRET} |
Затем мне пришлось внести несколько изменений в пару основных файлов Laravel, чтобы сделать его совместимым с последним Pusher SDK. Конечно, я не рекомендую вносить какие-либо изменения в фреймворк, но я просто остановлюсь на том, что нужно сделать.
Откройте файл vendor vendor/laravel/framework/src/Illuminate/PusherBroadcaster.php
. Просто замените фрагмент use Pusher;
на use Pusher\Pusher;
.
Затем давайте откроем файл vendor/laravel/framework/src/Illuminate/BroadcastManager.php
и сделаем аналогичное изменение в следующем фрагменте.
1 |
return new PusherBroadcaster( |
2 |
new \Pusher\Pusher($config['key'], $config['secret'], |
3 |
$config['app_id'], Arr::get($config, 'options', [])) |
4 |
);
|
Наконец, давайте включим службу broadcast в config/app.php
, удалив комментарий в следующей строке.
1 |
App\Providers\BroadcastServiceProvider::class, |
Итак, мы установили серверные библиотеки. В следующем разделе мы рассмотрим клиентские библиотеки, которые также должны быть установлены.
Библиотеки Pusher и Laravel Echo - Установка и настройка
В вещании ответственность стороны клиента заключается в том, чтобы подписаться на каналы и прослушивать желаемые события. Под капотом это реализовано, открывая новое соединение с сервером веб-сокетов.
К счастью, нам не нужно реализовывать какие-либо сложные вещи JavaScript для этого, поскольку Laravel уже предоставляет полезную клиентскую библиотеку Laravel Echo, которая помогает нам иметь дело с сокетами на стороне клиента. Кроме того, он поддерживает сервис Pusher, который мы собираемся использовать в этой статье.
Вы можете установить Laravel Echo, используя менеджер пакетов NPM. Конечно, вам нужно установить node и npm в первую очередь, если у вас их нет. Остальное довольно просто, как будет показано в следующем фрагменте.
1 |
$npm install laravel-echo |
Нам интересен файл node_modules/laravel-echo/dist/echo.js
, который вы должны скопировать в public/echo.js
.
Да, я понимаю, это наверно перебор, получить всего один JavaScript файл. Если вы не хотите проходить это упражнение, вы можете загрузить файл echo.js
из моего GitHub.
И с этим мы закончили настройку наших клиентских библиотек.
Настройка исходного файла
Напомним, что мы говорили о настройке приложения, которое позволяет пользователям нашего приложения отправлять сообщения друг другу. С другой стороны, мы отправим уведомления пользователям, которые вошли в систему, когда они получают новое сообщение от других пользователей.
В этом разделе мы создадим файлы, необходимые для реализации используемого use case.
Для начала давайте создадим модель Message
, в которой хранятся сообщения, отправленные пользователями друг другу.
1 |
$php artisan make:model Message --migration |
Нам также необходимо добавить несколько полей, например, to
, from
и message
в нашу таблицу сообщений. Итак, давайте изменим файл миграции перед запуском команды migrate.
1 |
<?php
|
2 |
|
3 |
use Illuminate\Support\Facades\Schema; |
4 |
use Illuminate\Database\Schema\Blueprint; |
5 |
use Illuminate\Database\Migrations\Migration; |
6 |
|
7 |
class CreateMessagesTable extends Migration |
8 |
{
|
9 |
/**
|
10 |
* Run the migrations.
|
11 |
*
|
12 |
* @return void
|
13 |
*/
|
14 |
public function up() |
15 |
{
|
16 |
Schema::create('messages', function (Blueprint $table) { |
17 |
$table->increments('id'); |
18 |
$table->integer('from', FALSE, TRUE); |
19 |
$table->integer('to', FALSE, TRUE); |
20 |
$table->text('message'); |
21 |
$table->timestamps(); |
22 |
});
|
23 |
}
|
24 |
|
25 |
/**
|
26 |
* Reverse the migrations.
|
27 |
*
|
28 |
* @return void
|
29 |
*/
|
30 |
public function down() |
31 |
{
|
32 |
Schema::dropIfExists('messages'); |
33 |
}
|
34 |
}
|
Теперь давайте запустим команду migrate, которая создает таблицу сообщений в базе данных.
1 |
$php artisan migrate
|
Всякий раз, когда вы хотите поднять пользовательское событие в Laravel, вы должны создать класс для этого события. В зависимости от типа события, Laravel реагирует соответственно и предпринимает необходимые действия.
Если событие является обычным событием, Laravel вызывает связанные классы слушателей. С другой стороны, если событие имеет тип вещания, Laravel отправляет это событие на сервер веб-сокета, который настроен в файле config/broadcasting.php
.
Когда мы используем сервис Pusher в нашем примере, Laravel отправит события на сервер Pusher.
Давайте используем следующую команду artisan для создания настраиваемого класса событий - NewMessageNotification
.
1 |
$php artisan make:event NewMessageNotification
|
Это должно создать класс app/Events/NewMessageNotification.php
. Давайте заменим содержимое этого файла следующим.
1 |
<?php
|
2 |
|
3 |
namespace App\Events; |
4 |
|
5 |
use Illuminate\Broadcasting\Channel; |
6 |
use Illuminate\Queue\SerializesModels; |
7 |
use Illuminate\Broadcasting\PrivateChannel; |
8 |
use Illuminate\Broadcasting\PresenceChannel; |
9 |
use Illuminate\Broadcasting\InteractsWithSockets; |
10 |
use Illuminate\Contracts\Broadcasting\ShouldBroadcastNow; |
11 |
use App\Message; |
12 |
|
13 |
class NewMessageNotification implements ShouldBroadcastNow |
14 |
{
|
15 |
use SerializesModels; |
16 |
|
17 |
public $message; |
18 |
|
19 |
/**
|
20 |
* Create a new event instance.
|
21 |
*
|
22 |
* @return void
|
23 |
*/
|
24 |
public function __construct(Message $message) |
25 |
{
|
26 |
$this->message = $message; |
27 |
}
|
28 |
|
29 |
/**
|
30 |
* Get the channels the event should broadcast on.
|
31 |
*
|
32 |
* @return Channel|array
|
33 |
*/
|
34 |
public function broadcastOn() |
35 |
{
|
36 |
return new PrivateChannel('user.'.$this->message->to); |
37 |
}
|
38 |
}
|
Важно отметить, что класс NewMessageNotification
реализует интерфейс ShouldBroadcastNow
. Таким образом, когда мы поднимаем событие, Laravel знает, что это событие должно быть транслировано.
Фактически, вы также можете реализовать интерфейс ShouldBroadcast
, а Laravel добавляет событие в очередь событий. Оно будет обработано работником очереди событий, когда у него появится такая возможность. В нашем случае мы хотим передать его прямо сейчас, поэтому мы использовали интерфейс ShouldBroadcastNow
.
В нашем случае мы хотим отобразить сообщение, полученное пользователем, и, таким образом, мы передали модель Message
в аргументе конструктора. Таким образом, данные будут переданы вместе с событием.
Затем существует метод broadcastOn
, который определяет имя канала, по которому будет транслироваться событие. В нашем случае мы использовали приватный канал, поскольку мы хотим ограничить передачу событий зарегистрированным пользователям.
Значение $this->message->to
относится к идентификатору пользователя, которому будет транслироваться событие. Таким образом имя канала подобно user.{USER_ID}
.
В случае частных каналов клиент должен пройти аутентификацию до установления соединения с сервером веб-сокетов. Это гарантирует, что события, которые транслируются на частных каналах, отправляются только для аутентифицированных клиентов. В нашем случае это означает, что только зарегистрированные пользователи смогут подписаться на наш канал user.{USER_ID}
.
Если вы используете клиентскую библиотеку Laravel Echo для подписки на канал, вам повезло! Она автоматически заботится о части аутентификации, и вам просто нужно определить маршруты каналов.
Давайте продолжим и добавим маршрут для нашего частного канала в файле routes/channels.php
.
1 |
<?php
|
2 |
|
3 |
/*
|
4 |
|--------------------------------------------------------------------------
|
5 |
| Broadcast Channels
|
6 |
|--------------------------------------------------------------------------
|
7 |
|
|
8 |
| Here you may register all of the event broadcasting channels that your
|
9 |
| application supports. The given channel authorization callbacks are
|
10 |
| used to check if an authenticated user can listen to the channel.
|
11 |
|
|
12 |
*/
|
13 |
|
14 |
Broadcast::channel('App.User.{id}', function ($user, $id) { |
15 |
return (int) $user->id === (int) $id; |
16 |
});
|
17 |
|
18 |
Broadcast::channel('user.{toUserId}', function ($user, $toUserId) { |
19 |
return $user->id == $toUserId; |
20 |
});
|
Как вы можете видеть, мы определили маршрут user.{toUserId}
для нашего приватного канала.
Второй аргумент метода канала должен быть анонимной функцией. Laravel автоматически передает зарегистрированного пользователя в качестве первого аргумента функции, а второй аргумент обычно выбирается из имени канала.
Когда клиент пытается подписаться на приватный канал user.{USER_ID}
, библиотека Laravel Echo выполняет необходимую аутентификацию в фоновом режиме с помощью объекта XMLHttpRequest или более широко известного как XHR.
Таким образом мы закончили настройку, поэтому давайте продолжим и протестируем ее.
Настройка Front-End
В этом разделе мы создадим файлы, необходимые для тестирования нашего use-case.
Давайте продолжим и создадим файл контроллера в app/Http/Controllers/MessageController.php
со следующим содержимым.
1 |
<?php
|
2 |
namespace App\Http\Controllers; |
3 |
|
4 |
use App\Http\Controllers\Controller; |
5 |
use App\Message; |
6 |
use App\Events\NewMessageNotification; |
7 |
use Illuminate\Support\Facades\Auth; |
8 |
|
9 |
class MessageController extends Controller |
10 |
{
|
11 |
public function __construct() { |
12 |
$this->middleware('auth'); |
13 |
}
|
14 |
|
15 |
public function index() |
16 |
{
|
17 |
$user_id = Auth::user()->id; |
18 |
$data = array('user_id' => $user_id); |
19 |
|
20 |
return view('broadcast', $data); |
21 |
}
|
22 |
|
23 |
public function send() |
24 |
{
|
25 |
// ...
|
26 |
|
27 |
// message is being sent
|
28 |
$message = new Message; |
29 |
$message->setAttribute('from', 1); |
30 |
$message->setAttribute('to', 2); |
31 |
$message->setAttribute('message', 'Demo message from user 1 to user 2'); |
32 |
$message->save(); |
33 |
|
34 |
// want to broadcast NewMessageNotification event
|
35 |
event(new NewMessageNotification($message)); |
36 |
|
37 |
// ...
|
38 |
}
|
39 |
}
|
В методе index
мы используем представление broadcast
, поэтому давайте создадим файл представлений resources/views/broadcast.blade.php
.
1 |
<!DOCTYPE html>
|
2 |
<html lang="{{ app()->getLocale() }}"> |
3 |
<head>
|
4 |
<meta charset="utf-8"> |
5 |
<meta http-equiv="X-UA-Compatible" content="IE=edge"> |
6 |
<meta name="viewport" content="width=device-width, initial-scale=1"> |
7 |
|
8 |
<!-- CSRF Token -->
|
9 |
<meta name="csrf-token" content="{{ csrf_token() }}"> |
10 |
|
11 |
<title>Test</title> |
12 |
|
13 |
<!-- Styles -->
|
14 |
<link href="{{ asset('css/app.css') }}" rel="stylesheet"> |
15 |
</head>
|
16 |
<body>
|
17 |
<div id="app"> |
18 |
<nav class="navbar navbar-default navbar-static-top"> |
19 |
<div class="container"> |
20 |
<div class="navbar-header"> |
21 |
|
22 |
<!-- Collapsed Hamburger -->
|
23 |
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#app-navbar-collapse"> |
24 |
<span class="sr-only">Toggle Navigation</span> |
25 |
<span class="icon-bar"></span> |
26 |
<span class="icon-bar"></span> |
27 |
<span class="icon-bar"></span> |
28 |
</button>
|
29 |
|
30 |
<!-- Branding Image -->
|
31 |
<a class="navbar-brand123" href="{{ url('/') }}"> |
32 |
Test |
33 |
</a>
|
34 |
</div>
|
35 |
|
36 |
<div class="collapse navbar-collapse" id="app-navbar-collapse"> |
37 |
<!-- Left Side Of Navbar -->
|
38 |
<ul class="nav navbar-nav"> |
39 |
|
40 |
</ul>
|
41 |
|
42 |
<!-- Right Side Of Navbar -->
|
43 |
<ul class="nav navbar-nav navbar-right"> |
44 |
<!-- Authentication Links -->
|
45 |
@if (Auth::guest()) |
46 |
<li><a href="{{ route('login') }}">Login</a></li> |
47 |
<li><a href="{{ route('register') }}">Register</a></li> |
48 |
@else |
49 |
<li class="dropdown"> |
50 |
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false"> |
51 |
{{ Auth::user()->name }} <span class="caret"></span> |
52 |
</a>
|
53 |
|
54 |
<ul class="dropdown-menu" role="menu"> |
55 |
<li>
|
56 |
<a href="{{ route('logout') }}" |
57 |
onclick="event.preventDefault(); |
58 |
document.getElementById('logout-form').submit();"> |
59 |
Logout |
60 |
</a>
|
61 |
|
62 |
<form id="logout-form" action="{{ route('logout') }}" method="POST" style="display: none;"> |
63 |
{{ csrf_field() }} |
64 |
</form>
|
65 |
</li>
|
66 |
</ul>
|
67 |
</li>
|
68 |
@endif |
69 |
</ul>
|
70 |
</div>
|
71 |
</div>
|
72 |
</nav>
|
73 |
|
74 |
<div class="content"> |
75 |
<div class="m-b-md"> |
76 |
New notification will be alerted realtime! |
77 |
</div>
|
78 |
</div>
|
79 |
</div>
|
80 |
|
81 |
<!-- receive notifications -->
|
82 |
<script src="{{ asset('js/echo.js') }}"></script> |
83 |
|
84 |
<script src="https://js.pusher.com/4.1/pusher.min.js"></script> |
85 |
|
86 |
<script>
|
87 |
Pusher.logToConsole = true; |
88 |
|
89 |
window.Echo = new Echo({ |
90 |
broadcaster: 'pusher', |
91 |
key: 'c91c1b7e8c6ece46053b', |
92 |
cluster: 'ap2', |
93 |
encrypted: true, |
94 |
logToConsole: true |
95 |
});
|
96 |
|
97 |
Echo.private('user.{{ $user_id }}') |
98 |
.listen('NewMessageNotification', (e) => { |
99 |
alert(e.message.message); |
100 |
});
|
101 |
</script>
|
102 |
<!-- receive notifications -->
|
103 |
</body>
|
104 |
</html>
|
И, конечно же, нам нужно добавить маршруты и в файл routes/web.php
.
1 |
Route::get('message/index', 'MessageController@index'); |
2 |
Route::get('message/send', 'MessageController@send'); |
В методе конструктора класса контроллера вы можете видеть, что мы использовали middleware auth
, чтобы убедиться, что методы контроллера доступны только зарегистрированным пользователям.
Далее, есть метод index
, который отображает вьюшку broadcast
. Давайте возьмем самый важный код в файле отображения.
1 |
<!-- receive notifications -->
|
2 |
<script src="{{ asset('js/echo.js') }}"></script> |
3 |
|
4 |
<script src="https://js.pusher.com/4.1/pusher.min.js"></script> |
5 |
|
6 |
<script>
|
7 |
Pusher.logToConsole = true; |
8 |
|
9 |
window.Echo = new Echo({ |
10 |
broadcaster: 'pusher', |
11 |
key: 'c91c1b7e8c6ece46053b', |
12 |
cluster: 'ap2', |
13 |
encrypted: true, |
14 |
logToConsole: true |
15 |
});
|
16 |
|
17 |
Echo.private('user.{{ $user_id }}') |
18 |
.listen('NewMessageNotification', (e) => { |
19 |
alert(e.message.message); |
20 |
});
|
21 |
</script>
|
22 |
<!-- receive notifications -->
|
Во-первых, мы загружаем необходимые клиентские библиотеки, Laravel Echo и Pusher, что позволяет нам открыть соединение веб-сокетов с сервером веб-сокетов Pusher.
Затем мы создаем экземпляр Echo, предоставляя Pusher в качестве нашего адаптера и другую необходимую информацию, связанную с Pusher.
Двигаясь дальше, мы используем метод private Echo для подписки на пользователя на приватный канал user.{USER_ID}
. Как мы обсуждали ранее, клиент должен пройти аутентификацию перед подпиской на приватный канал. Таким образом, объект Echo выполняет необходимую аутентификацию, отправляя XHR в фоновом режиме с необходимыми параметрами. Наконец, Laravel пытается найти маршрут user.{USER_ID}
, и он должен соответствовать маршруту, который мы определили в файле routes/channels.php
.
Если все пройдет хорошо, у вас должно быть соединение с веб-сокетом с сервером веб-сокетов Pusher, и он слушает события на канале user.{USER_ID}
! С этого момента мы получим все входящие события на этом канале.
В нашем случае мы хотим прослушать событие NewMessageNotification
, и поэтому мы использовали метод listen
объекта Echo
. Чтобы все было просто, мы просто выводит сообщение с помощью alert.
Таким образом, это была настройка для приема событий с сервера веб-сокетов. Затем мы рассмотрим метод send
в файле контроллера, который вызывает событие трансляции.
Давайте быстро введем код метода send
.
1 |
public function send() |
2 |
{
|
3 |
// ...
|
4 |
|
5 |
// message is being sent
|
6 |
$message = new Message; |
7 |
$message->setAttribute('from', 1); |
8 |
$message->setAttribute('to', 2); |
9 |
$message->setAttribute('message', 'Demo message from user 1 to user 2'); |
10 |
$message->save(); |
11 |
|
12 |
// want to broadcast NewMessageNotification event
|
13 |
event(new NewMessageNotification($message)); |
14 |
|
15 |
// ...
|
16 |
}
|
В нашем случае мы будем уведомлять зарегистрированных пользователей, когда они получат новое сообщение. Поэтому мы попытались воспроизвести это поведение в методе send
.
Затем мы использовали функцию помощник event
, чтобы создать событие NewMessageNotification
. Поскольку событие NewMessageNotification
имеет тип ShouldBroadcastNow
, Laravel загружает конфигурацию broadcast по умолчанию из файла config/broadcasting.php
. Наконец, он транслирует событие NewMessageNotification
на настроенный сервер веб-сокетов на канал user.{USER_ID}
.
В нашем случае событие будет транслироваться на сервер веб-сокетов Pusher на канал user.{USER_ID}
. Если идентификатор пользователя-получателя равен 1
, событие будет транслироваться по каналу user.1
.
Как мы обсуждали ранее, у нас уже есть настройка, которая прослушивает события на этом канале, поэтому она должна иметь возможность получать это событие, а окно предупреждения отображается пользователю!
Давайте продолжим и рассмотрим, как вы должны тестировать этот use-case, который мы создали.
Откройте в вашем браузере адрес http://your-laravel-site-domain/message/index. Если вы еще не вошли в систему, вы будете перенаправлены на экран входа в систему. После того, как вы вошли в систему, вы должны увидеть представление, которое мы определили ранее, пока ничего интересного.
На самом деле, Laravel уже проделал довольно много работы на заднем плане для вас. Поскольку мы включили параметр Pusher.logToConsole
, предоставленный клиентской библиотекой Pusher, то для целей отладки он регистрирует все события в консоли браузера. Посмотрим, что регистрируется на консоли при доступе к странице http://your-laravel-site-domain/message/index.
1 |
Pusher : State changed : initialized -> connecting |
2 |
|
3 |
Pusher : Connecting : {"transport":"ws","url":"wss://ws-ap2.pusher.com:443/app/c91c1b7e8c6ece46053b?protocol=7&client=js&version=4.1.0&flash=false"} |
4 |
|
5 |
Pusher : Connecting : {"transport":"xhr_streaming","url":"https://sockjs-ap2.pusher.com:443/pusher/app/c91c1b7e8c6ece46053b?protocol=7&client=js&version=4.1.0"} |
6 |
|
7 |
Pusher : State changed : connecting -> connected with new socket ID 1386.68660 |
8 |
|
9 |
Pusher : Event sent : {"event":"pusher:subscribe","data":{"auth":"c91c1b7e8c6ece46053b:cd8b924580e2cbbd2977fd4ef0d41f1846eb358e9b7c327d89ff6bdc2de9082d","channel":"private-user.2"}} |
10 |
|
11 |
Pusher : Event recd : {"event":"pusher_internal:subscription_succeeded","data":{},"channel":"private-user.2"} |
12 |
|
13 |
Pusher : No callbacks on private-user.2 for pusher:subscription_succeeded |
Было открыто соединение с веб-сокетом с сервером веб-сокетов Pusher и была подписка на прослушивание событий на приватном канале. Конечно, у вас может быть другое имя канала в вашем случае на основе идентификатора пользователя, с которым вы вошли в систему. Теперь давайте оставим эту страницу открытой, и перейдем, чтобы протестировать метод send
.
Затем давайте откроем URL-адрес http://your-laravel-site-domain/message/send на другой вкладке или в другом браузере. Если вы собираетесь использовать другой браузер, вам нужно войти в систему, чтобы иметь доступ к этой странице.
Как только вы откроете страницу http://your-laravel-site-domain/message/send, вы сможете увидеть предупреждающее сообщение на другой вкладке по адресу http://your-laravel-site-domain/message/index.
Перейдем к консоли, чтобы увидеть, что только что произошло.
1 |
Pusher : Event recd : {"event":"App\\Events\\NewMessageNotification","data":{"message":{"id":57,"from":1,"to":2,"message":"Demo message from user 1 to user 2","created_at":"2018-01-13 07:10:10","updated_at":"2018-01-13 07:10:10"}},"channel":"private-user.2"} |
Как вы можете видеть, он сообщает вам, что вы только что получили событие App\Events\NewMessageNotification
от сервера веб-сокетов Pusher на канале private-user.2
.
Фактически, вы можете видеть, что происходит там, на стороне Pusher. Перейдите на свою учетную запись Pusher и перейдите к своему приложению. В Debug Console вы должны иметь возможность видеть сообщения, которые регистрируются.



И на этом мы подходим к концу этой статьи! Надеюсь, это было не так уж и много, так как я пытался максимально все упростить.
Заключение
Сегодня мы рассмотрели одну из наименее обсуждаемых особенностей Laravel- broadcasting. Она позволяет отправлять уведомления в режиме реального времени с помощью веб-сокетов. На протяжении всей этой статьи мы построили реальный пример, демонстрирующий вышеупомянутую концепцию.
Да, я знаю, придется много чего понять в этой статье, поэтому не стесняйтесь использовать фид комментариев ниже, если у вас возникнут проблемы во время реализации.