1. Глубже в детали
  2. Вещание

Введение

Во многих современных веб-приложениях используются веб-сокеты для реализации мгновенных, обновляющихся в реальном времени пользовательских интерфейсов. Когда какие-то данные обновляются на сервере, обычно отправляется сообщение по соединению веб-сокета для обработки клиентом. Веб-сокеты предоставляют более эффективную альтернативу постоянному опросу сервера вашего приложения для отслеживания изменений данных, которые должны отображаться в вашем пользовательском интерфейсе.

Например, представьте, что ваше приложение может экспортировать данные пользователя в CSV-файл и отправить его по электронной почте. Однако создание этого CSV-файла занимает несколько минут, поэтому вы решаете создать и отправить CSV внутри очередной задачи. Когда CSV-файл создан и отправлен пользователю, мы можем использовать событийную трансляцию для отправки события App\Events\UserDataExported, которое принимает наше JavaScript-приложение. Как только событие будет получено, мы можем отобразить сообщение пользователю, что его CSV отправлено ему, не заставляя его обновлять страницу.

Чтобы помочь вам создавать подобные функции, Laravel упрощает процесс "трансляции" ваших серверных событий Laravel events через соединение WebSocket. Трансляция ваших событий Laravel позволяет вам использовать одни и те же имена событий и данные между вашим серверным приложением Laravel и вашим клиентским JavaScript-приложением.

Основные концепции трансляции просты: клиенты подключаются к именованным каналам на фронтенде, в то время как ваше приложение Laravel транслирует события в эти каналы на бэкенде. Эти события могут содержать любые дополнительные данные, которые вы хотите сделать доступными на фронтенде.

Поддерживаемые драйверы

По умолчанию Laravel включает два драйвера серверной трансляции на выбор: Pusher Channels и Ably. Тем не менее, пакеты, созданные сообществом, такие как laravel-websockets и soketi, предоставляют дополнительные драйверы трансляции, которые не требуют коммерческих поставщиков вещания.

Примечание Прежде чем приступить к трансляции событий, убедитесь, что вы прочли документацию Laravel по событиям и слушателям.

Установка на сервере

Для начала использования событийной трансляции Laravel нам нужно выполнить некоторую конфигурацию в приложении Laravel, а также установить несколько пакетов.

Трансляция событий выполняется драйвером серверной трансляции, который транслирует ваши события Laravel так, чтобы Laravel Echo (библиотека JavaScript) могла их получить в браузерном клиенте. Не волнуйтесь, мы рассмотрим каждую часть процесса установки пошагово.

Настройка

Вся конфигурация трансляции событий вашего приложения хранится в конфигурационном файле config/broadcasting.php. Laravel поддерживает несколько драйверов трансляции из коробки: Pusher Channels, Redis и драйвер log для локальной разработки и отладки. Кроме того, включен драйвер null, который позволяет полностью отключить трансляцию во время тестирования. В файле конфигурации config/broadcasting.php приведен пример конфигурации для каждого из этих драйверов.

Поставщик услуг трансляции

Прежде чем транслировать какие-либо события, вам сначала нужно зарегистрировать App\Providers\BroadcastServiceProvider. В новых приложениях Laravel вам нужно только раскомментировать этот провайдер в массиве providers файла конфигурации config/app.php. Этот BroadcastServiceProvider содержит код, необходимый для регистрации маршрутов и обратных вызовов авторизации трансляции.

Настройка очереди

Вам также нужно настроить и запустить очередной обработчик. Вся трансляция событий выполняется с использованием отложенных задач, чтобы время ответа вашего приложения серьезно не страдало от трансляции событий.

Каналы Pusher

Если вы планируете транслировать ваши события с использованием Pusher Channels, вы должны установить SDK Pusher Channels PHP с помощью менеджера пакетов Composer:

composer require pusher/pusher-php-server

Затем вам следует настроить учетные данные Pusher Channels в файле конфигурации config/broadcasting.php. Пример конфигурации Pusher Channels уже включен в этот файл, что позволяет быстро указать ключ, секрет и идентификатор приложения. Обычно эти значения должны быть установлены с помощью переменных окружения PUSHER_APP_KEY, PUSHER_APP_SECRET и PUSHER_APP_ID переменных окружения:

PUSHER_APP_ID=your-pusher-app-id
PUSHER_APP_KEY=your-pusher-key
PUSHER_APP_SECRET=your-pusher-secret
PUSHER_APP_CLUSTER=mt1

Конфигурация pusher файла config/broadcasting.php также позволяет указать дополнительные options, поддерживаемые Channels, такие как кластер.

Затем вам нужно изменить драйвер трансляции на pusher в вашем файле .env:

BROADCAST_DRIVER=pusher

Наконец, вы готовы установить и настроить Laravel Echo, который будет принимать транслируемые события на стороне клиента.

Открытые альтернативы Pusher

laravel-websockets и soketi предоставляют совместимые с Pusher серверы WebSocket для Laravel. Эти пакеты позволяют вам использовать все возможности трансляции Laravel без коммерческого поставщика WebSocket. Для получения дополнительной информации о установке и использовании этих пакетов обратитесь к нашей документации по альтернативам с открытым исходным кодом.

Ably

Примечание В документации ниже рассматривается использование Ably в режиме совместимости с Pusher. Однако команда Ably рекомендует и поддерживает драйвер и клиент Echo, способные использовать уникальные возможности, предлагаемые Ably. Для получения дополнительной информации о использовании поддерживаемых Ably драйверов обратитесь к документации по трансляции в Laravel от Ably.

Если вы планируете транслировать свои события с использованием Ably, вы должны установить SDK Ably PHP с использованием менеджера пакетов Composer:

composer require ably/ably-php

Затем вам следует настроить учетные данные Ably в конфигурационном файле config/broadcasting.php. Пример конфигурации Ably уже включен в этот файл, что позволяет быстро указать ваш ключ. Обычно это значение должно быть установлено с использованием переменной окружения ABLY_KEY переменной окружения:

ABLY_KEY=your-ably-key

Затем вам потребуется изменить драйвер трансляции на ably в вашем файле .env:

BROADCAST_DRIVER=ably

Наконец, вы готовы установить и настроить Laravel Echo, который будет получать транслируемые события на стороне клиента.

Открытые альтернативы

PHP

Пакет laravel-websockets - это чистый PHP-пакет WebSocket, совместимый с Pusher, для Laravel. Этот пакет позволяет использовать все возможности трансляции Laravel без коммерческого поставщика WebSocket. Для получения дополнительной информации об установке и использовании этого пакета ознакомьтесь с официальной документацией.

Node

Soketi - это сервер WebSocket на основе Node, совместимый с Pusher, для Laravel. Под капотом Soketi использует µWebSockets.js для обеспечения масштабируемости и скорости. Этот пакет позволяет использовать все возможности трансляции Laravel без коммерческого поставщика WebSocket. Для получения дополнительной информации об установке и использовании этого пакета ознакомьтесь с официальной документацией.

Установка на клиенте

Каналы Pusher

Laravel Echo - это библиотека JavaScript, которая упрощает подписку на каналы и прослушивание событий, транслируемых вашим драйвером трансляции с сервера. Вы можете установить Echo с помощью менеджера пакетов NPM. В этом примере мы также установим пакет pusher-js, так как будем использовать транслятор Pusher Channels:

npm install --save-dev laravel-echo pusher-js

После установки Echo вы готовы создать новый экземпляр Echo в вашем JavaScript-приложении. Отличным местом для этого является конец файла resources/js/bootstrap.js, который поставляется с фреймворком Laravel. По умолчанию в этом файле уже предоставлен пример конфигурации Echo - вам просто нужно его раскомментировать:

import Echo from 'laravel-echo';
import Pusher from 'pusher-js';
 
window.Pusher = Pusher;
 
window.Echo = new Echo({
broadcaster: 'pusher',
key: import.meta.env.VITE_PUSHER_APP_KEY,
cluster: import.meta.env.VITE_PUSHER_APP_CLUSTER,
forceTLS: true
});

После того как вы раскомментируете и настроите конфигурацию Echo согласно вашим потребностям, вы можете скомпилировать ресурсы вашего приложения:

npm run dev

Примечание Для получения дополнительной информации о компиляции JavaScript-активов вашего приложения ознакомьтесь с документацией по Vite.

Использование существующего экземпляра клиента

Если у вас уже есть предварительно настроенный клиент Pusher Channels, который вы хотели бы использовать Echo, вы можете передать его в Echo с помощью опции конфигурации client:

import Echo from 'laravel-echo';
import Pusher from 'pusher-js';
 
const options = {
broadcaster: 'pusher',
key: 'your-pusher-channels-key'
}
 
window.Echo = new Echo({
...options,
client: new Pusher(options.key, options)
});

Ably

Примечание В документации ниже рассматривается использование Ably в режиме совместимости с Pusher. Однако команда Ably рекомендует и поддерживает драйвер и клиент Echo, способные использовать уникальные возможности, предлагаемые Ably. Для получения дополнительной информации о использовании поддерживаемых Ably драйверов обратитесь к документации по трансляции в Laravel от Ably.

Laravel Echo - это библиотека JavaScript, которая упрощает подписку на каналы и прослушивание событий, транслируемых вашим драйвером трансляции с сервера. Вы можете установить Echo с помощью менеджера пакетов NPM. В этом примере мы также установим пакет pusher-js.

Возможно, вы задаетесь вопросом, почему мы устанавливаем библиотеку pusher-js, даже если мы используем Ably для трансляции наших событий. К счастью, Ably включает режим совместимости с Pusher, который позволяет нам использовать протокол Pusher при прослушивании событий в нашем клиентском приложении:

npm install --save-dev laravel-echo pusher-js

Прежде чем продолжить, вы должны включить поддержку протокола Pusher в настройках вашего приложения Ably. Вы можете включить эту функцию в разделе "Настройки адаптера протокола" на панели настроек вашего приложения Ably.

После установки Echo вы готовы создать новый экземпляр Echo в вашем JavaScript-приложении. Отличным местом для этого является конец файла resources/js/bootstrap.js, который поставляется с фреймворком Laravel. По умолчанию в этом файле уже предоставлен пример конфигурации Echo; однако конфигурация по умолчанию в файле bootstrap.js предназначена для Pusher. Вы можете скопировать приведенную ниже конфигурацию для перехода от конфигурации Pusher к Ably:

import Echo from 'laravel-echo';
import Pusher from 'pusher-js';
 
window.Pusher = Pusher;
 
window.Echo = new Echo({
broadcaster: 'pusher',
key: import.meta.env.VITE_ABLY_PUBLIC_KEY,
wsHost: 'realtime-pusher.ably.io',
wsPort: 443,
disableStats: true,
encrypted: true,
});

Обратите внимание, что наша конфигурация Ably Echo ссылается на переменную окружения VITE_ABLY_PUBLIC_KEY. Значением этой переменной должен быть ваш открытый ключ Ably. Ваш открытый ключ - это часть вашего ключа Ably, которая находится перед символом :.

После того как вы раскомментируете и настроите конфигурацию Echo согласно вашим потребностям, вы можете скомпилировать ресурсы вашего приложения:

npm run dev

Примечание Для получения дополнительной информации о компиляции JavaScript-активов вашего приложения ознакомьтесь с документацией по Vite.

Обзор концепций

Событийная трансляция Laravel позволяет передавать ваши события Laravel с серверной стороны на клиентскую сторону вашего приложения JavaScript с использованием подхода, основанного на драйверах WebSockets. В настоящее время Laravel поставляется с драйверами Pusher Channels и Ably. События могут быть легко использованы на стороне клиента с использованием JavaScript-пакета Laravel Echo.

События транслируются через "каналы", которые могут быть общедоступными или частными. Любой посетитель вашего приложения может подписаться на общедоступный канал без какой-либо аутентификации или авторизации; однако для подписки на частный канал пользователь должен быть аутентифицирован и авторизован для прослушивания этого канала.

Примечание Если вы хотите изучить открытые альтернативы Pusher, ознакомьтесь с разделом открытых альтернатив.

Использование примера приложения

Прежде чем мы рассмотрим каждый компонент трансляции событий, давайте рассмотрим общий обзор, используя в качестве примера интернет-магазин.

Предположим, что у нас в приложении есть страница, позволяющая пользователям просматривать статус доставки своих заказов. Допустим также, что событие OrderShipmentStatusUpdated срабатывает, когда обновляется статус доставки:

use App\Events\OrderShipmentStatusUpdated;
 
OrderShipmentStatusUpdated::dispatch($order);

Интерфейс ShouldBroadcast

Когда пользователь просматривает один из своих заказов, мы не хотим, чтобы ему приходилось обновлять страницу для просмотра обновлений статуса. Вместо этого мы хотим транслировать обновления в приложение по мере их создания. Итак, нам нужно пометить событие OrderShipmentStatusUpdated интерфейсом ShouldBroadcast. Это указывает Laravel транслировать событие при его срабатывании:

<?php
 
namespace App\Events;
 
use App\Models\Order;
use Illuminate\Broadcasting\Channel;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Broadcasting\PresenceChannel;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Queue\SerializesModels;
 
class OrderShipmentStatusUpdated implements ShouldBroadcast
{
/**
* Экземпляр заказа.
*
* @var \App\Order
*/
public $order;
}

Интерфейс ShouldBroadcast требует, чтобы наше событие определяло метод broadcastOn. Этот метод отвечает за возврат каналов, на которых должно быть транслировано событие. Пустой заготовкой этого метода уже предоставлено в сгенерированных классах событий, поэтому нам нужно только заполнить его детали. Мы хотим, чтобы только создатель заказа мог просматривать обновления статуса, поэтому мы будем транслировать событие на частный канал, привязанный к заказу:

use Illuminate\Broadcasting\Channel;
use Illuminate\Broadcasting\PrivateChannel;
 
/**
* Получить канал, на который должно транслироваться событие.
*/
public function broadcastOn(): Channel
{
return new PrivateChannel('orders.'.$this->order->id);
}

Если вы хотите, чтобы событие транслировалось на несколько каналов, вы можете вернуть array:

use Illuminate\Broadcasting\PrivateChannel;
 
/**
* Получить каналы, на которые должно транслироваться событие.
*
* @return array<int, \Illuminate\Broadcasting\Channel>
*/
public function broadcastOn(): array
{
return [
new PrivateChannel('orders.'.$this->order->id),
// ...
];
}

Авторизация каналов

Помните, что пользователи должны быть авторизованы для прослушивания частных каналов. Мы можем определить правила авторизации наших каналов в файле routes/channels.php вашего приложения. В этом примере нам нужно убедиться, что любой пользователь, пытающийся прослушивать частный канал orders.1, действительно является создателем заказа:

use App\Models\Order;
use App\Models\User;
 
Broadcast::channel('orders.{orderId}', function (User $user, int $orderId) {
return $user->id === Order::findOrNew($orderId)->user_id;
});

Метод channel принимает два аргумента: имя канала и обратный вызов, который возвращает true или false, указывая, авторизован ли пользователь для прослушивания канала.

Все обратные вызовы авторизации получают в качестве первого аргумента текущего аутентифицированного пользователя и любые дополнительные параметры с подстановочными символами в качестве последующих аргументов. В этом примере мы используем заполнитель {orderId}, чтобы указать, что часть "ID" имени канала является подстановочным символом.

Ожидание трансляций событий

Теперь все, что остается, - это прослушать событие в нашем JavaScript-приложении. Мы можем сделать это с использованием Laravel Echo. Сначала мы используем метод private для подписки на частный канал. Затем мы можем использовать метод listen для прослушивания события OrderShipmentStatusUpdated. По умолчанию вещательное событие будет включать все общедоступные свойства события:

Echo.private(`orders.${orderId}`)
.listen('OrderShipmentStatusUpdated', (e) => {
console.log(e.order);
});

Определение транслируемых событий

Чтобы сообщить Laravel, что данное событие должно быть транслировано, вы должны реализовать интерфейс Illuminate\Contracts\Broadcasting\ShouldBroadcast в классе события. Этот интерфейс уже импортирован во все классы событий, сгенерированные фреймворком, поэтому вы можете легко добавить его к любому из ваших событий.

Интерфейс ShouldBroadcast требует реализации единственного метода: broadcastOn. Метод broadcastOn должен возвращать канал или массив каналов, на которых должно быть транслировано событие. Каналы должны быть экземплярами Channel, PrivateChannel или PresenceChannel. Экземпляры Channel представляют общедоступные каналы, на которые может подписаться любой пользователь, в то время как PrivateChannels и PresenceChannels представляют частные каналы, требующие авторизации каналов:

<?php
 
namespace App\Events;
 
use App\Models\User;
use Illuminate\Broadcasting\Channel;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Broadcasting\PresenceChannel;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Queue\SerializesModels;
 
class ServerCreated implements ShouldBroadcast
{
use SerializesModels;
 
/**
* Создать новый экземпляр события.
*/
public function __construct(
public User $user,
) {}
 
/**
* Получить каналы, на которые должно транслироваться событие.
*
* @return array<int, \Illuminate\Broadcasting\Channel>
*/
public function broadcastOn(): array
{
return [
new PrivateChannel('user.'.$this->user->id),
];
}
}

После реализации интерфейса ShouldBroadcast вам нужно только запустить событие, как обычно. Как только событие будет запущено, очередная задача автоматически транслирует событие с использованием вашего указанного драйвера трансляции.

Имя трансляции

По умолчанию Laravel будет транслировать событие с использованием имени класса события. Однако вы можете настроить имя трансляции, определив метод broadcastAs в событии:

/**
* Имя трансляции события.
*/
public function broadcastAs(): string
{
return 'server.created';
}

Если вы настраиваете имя трансляции с использованием метода broadcastAs, убедитесь, что ваш слушатель зарегистрирован с ведущим символом .. Это указывает Echo не добавлять пространство имен приложения к событию:

.listen('.server.created', function (e) {
....
});

Данные трансляции

Когда событие транслируется, все его public свойства автоматически сериализуются и передаются в качестве данных события, позволяя вам получить доступ к любым его общедоступным данным из вашего JavaScript-приложения. Так, например, если у вашего события есть единственное общедоступное свойство $user, содержащее экземпляр Eloquent модели, данные трансляции события будут следующими:

{
"user": {
"id": 1,
"name": "Patrick Stewart"
...
}
}

Однако, если вы хотите иметь более тонкий контроль над вашей трансляцией, вы можете добавить метод broadcastWith к вашему событию. Этот метод должен возвращать массив данных, которые вы хотите транслировать в качестве данных события:

/**
* Получить данные для трансляции.
*
* @return array<string, mixed>
*/
public function broadcastWith(): array
{
return ['id' => $this->user->id];
}

Очередь трансляции

По умолчанию каждое вещательное событие помещается в очередь по умолчанию для соединения очереди по умолчанию, указанного в вашем файле конфигурации queue.php. Вы можете настроить соединение и имя очереди, используемое вещателем, определив свойства connection и queue в вашем классе события:

/**
* Имя соединения очереди для трансляции события.
*
* @var string
*/
public $connection = 'redis';
 
/**
* Имя очереди, в которую следует поместить задание на трансляцию.
*
* @var string
*/
public $queue = 'default';

Кроме того, вы можете настроить имя очереди, определив метод broadcastQueue в вашем событии:

/**
* Имя очереди, в которую следует поместить задание на трансляцию.
*/
public function broadcastQueue(): string
{
return 'default';
}

Если вы хотите транслировать свое событие, используя очередь sync вместо драйвера очереди по умолчанию, вы можете реализовать интерфейс ShouldBroadcastNow вместо ShouldBroadcast:

<?php
 
use Illuminate\Contracts\Broadcasting\ShouldBroadcastNow;
 
class OrderShipmentStatusUpdated implements ShouldBroadcastNow
{
// ...
}

Условия трансляции

Иногда вы хотите транслировать свое событие только в том случае, если выполнено определенное условие. Вы можете определить эти условия, добавив метод broadcastWhen в ваш класс события:

/**
* Определить, следует ли транслировать это событие.
*/
public function broadcastWhen(): bool
{
return $this->order->value > 100;
}

Трансляции и транзакции базы данных

Когда события транслируются в пределах транзакций базы данных, они могут быть обработаны очередью до того, как транзакция базы данных будет подтверждена. Когда это происходит, любые обновления, сделанные вами в моделях или записях базы данных во время транзакции, могут еще не отразиться в базе данных. Кроме того, любые модели или записи базы данных, созданные в пределах транзакции, могут не существовать в базе данных. Если ваше событие зависит от этих моделей, при обработке задачи, транслирующей событие, могут возникнуть неожиданные ошибки.

Если параметр конфигурации after_commit соединения с очередью установлен в false, вы все равно можете указать, что определенное вещательное событие должно быть отправлено после подтверждения всех открытых транзакций базы данных, реализовав интерфейс ShouldDispatchAfterCommit в классе события:

<?php
 
namespace App\Events;
 
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Contracts\Events\ShouldDispatchAfterCommit;
use Illuminate\Queue\SerializesModels;
 
class ServerCreated implements ShouldBroadcast, ShouldDispatchAfterCommit
{
use SerializesModels;
}

Примечание Для получения дополнительной информации о работе с этими проблемами ознакомьтесь с документацией по очередным заданиям и транзакциям базы данных.

Авторизация каналов

Для частных каналов необходимо разрешение для прослушивания канала только аутентифицированным пользователем. Это достигается путем выполнения HTTP-запроса к вашему приложению Laravel с указанием имени канала и разрешения вашему приложению определить, может ли пользователь прослушивать этот канал. При использовании Laravel Echo HTTP-запрос на авторизацию подписок на частные каналы выполняется автоматически; однако вам нужно определить правильные маршруты для ответа на эти запросы.

Определение маршрутов авторизации

К счастью, Laravel упрощает определение маршрутов для ответа на запросы авторизации каналов. В App\Providers\BroadcastServiceProvider, включенном в ваше приложение Laravel, вы увидите вызов метода Broadcast::routes. Этот метод зарегистрирует маршрут /broadcasting/auth для обработки запросов авторизации:

Broadcast::routes();

Метод Broadcast::routes автоматически помещает свои маршруты в группу промежуточного программного обеспечения web; однако вы можете передать массив атрибутов маршрута методу, если хотите настроить назначенные атрибуты:

Broadcast::routes($attributes);

Настройка конечной точки авторизации

По умолчанию Echo будет использовать конечную точку /broadcasting/auth для авторизации доступа к каналу. Однако вы можете указать свою собственную конечную точку авторизации, передав конфигурационную опцию authEndpoint в ваш экземпляр Echo:

window.Echo = new Echo({
broadcaster: 'pusher',
// ...
authEndpoint: '/custom/endpoint/auth'
});

Настройка запроса авторизации

Вы можете настроить способ выполнения Echo запросов на авторизацию, предоставив собственный авторизатор при инициализации Echo:

window.Echo = new Echo({
// ...
authorizer: (channel, options) => {
return {
authorize: (socketId, callback) => {
axios.post('/api/broadcasting/auth', {
socket_id: socketId,
channel_name: channel.name
})
.then(response => {
callback(null, response.data);
})
.catch(error => {
callback(error);
});
}
};
},
})

Определение обратных вызовов авторизации

Затем нам нужно определить логику, которая действительно определит, может ли текущий аутентифицированный пользователь слушать данный канал. Это делается в файле routes/channels.php, который включен в ваше приложение. В этом файле вы можете использовать метод Broadcast::channel для регистрации обратных вызовов авторизации канала:

use App\Models\User;
 
Broadcast::channel('orders.{orderId}', function (User $user, int $orderId) {
return $user->id === Order::findOrNew($orderId)->user_id;
});

Метод channel принимает два аргумента: имя канала и обратный вызов, который возвращает true или false, указывая, авторизован ли пользователь слушать канал.

Все обратные вызовы авторизации получают текущего аутентифицированного пользователя в качестве первого аргумента и любые дополнительные параметры с подстановкой символов в качестве их последующих аргументов. В этом примере мы используем заполнитель {orderId} для указания того, что часть имени канала "ID" является подстановочным символом.

Вы можете просмотреть список колбэков авторизации ваших каналов в приложении, используя команду Artisan channel:list:

php artisan channel:list

Привязка модели обратного вызова авторизации

Как и HTTP-маршруты, маршруты каналов также могут использовать неявное и явное привязку моделей к маршруту. Например, вместо получения строки или числового идентификатора заказа, вы можете запросить фактический экземпляр модели Order:

use App\Models\Order;
use App\Models\User;
 
Broadcast::channel('orders.{order}', function (User $user, Order $order) {
return $user->id === $order->user_id;
});

Внимание В отличие от привязки модели HTTP-маршрута, привязка модели канала не поддерживает автоматического неявного ограничения привязки модели. Однако это редко становится проблемой, потому что большинство каналов можно ограничивать на основе уникального первичного ключа одной модели.

Аутентификация обратного вызова авторизации

Приватные и каналы трансляции по присутствию аутентифицируют текущего пользователя с использованием вашей системы аутентификации по умолчанию. Если пользователь не аутентифицирован, авторизация канала автоматически отклоняется, и обратный вызов авторизации никогда не выполняется. Однако вы можете назначить несколько пользовательских стражей, которые должны аутентифицировать входящий запрос, если это необходимо:

Broadcast::channel('channel', function () {
// ...
}, ['guards' => ['web', 'admin']]);

Определение классов каналов

Если ваше приложение использует множество различных каналов, ваш файл routes/channels.php может стать громоздким. Вместо использования замыканий для авторизации каналов вы можете использовать классы каналов. Чтобы создать класс канала, используйте команду Artisan make:channel. Эта команда поместит новый класс канала в каталог App/Broadcasting.

php artisan make:channel OrderChannel

Затем зарегистрируйте свой канал в файле routes/channels.php:

use App\Broadcasting\OrderChannel;
 
Broadcast::channel('orders.{order}', OrderChannel::class);

Наконец, вы можете разместить логику авторизации для вашего канала в методе join класса канала. Этот метод join будет содержать ту же логику, которую вы обычно размещали бы в замыкании авторизации канала. Вы также можете воспользоваться привязкой моделей к каналу:

<?php
 
namespace App\Broadcasting;
 
use App\Models\Order;
use App\Models\User;
 
class OrderChannel
{
/**
* Создать новый экземпляр канала.
*/
public function __construct()
{
// ...
}
 
/**
* Аутентифицировать доступ пользователя к каналу.
*/
public function join(User $user, Order $order): array|bool
{
return $user->id === $order->user_id;
}
}

Примечание Как и многие другие классы в Laravel, классы каналов будут автоматически разрешаться контейнером служб. Так что вы можете указывать зависимости, необходимые вашему каналу, в конструкторе.

Трансляция событий

После того как вы определили событие и пометили его интерфейсом ShouldBroadcast, вам нужно только запустить событие, используя метод диспетчера событий. Диспетчер событий заметит, что событие помечено интерфейсом ShouldBroadcast, и поставит его в очередь для трансляции:

use App\Events\OrderShipmentStatusUpdated;
 
OrderShipmentStatusUpdated::dispatch($order);

Только другим

При построении приложения, использующего трансляцию событий, вам иногда может потребоваться транслировать событие всем подписчикам данного канала, за исключением текущего пользователя. Вы можете сделать это, используя вспомогательную функцию broadcast и метод toOthers:

use App\Events\OrderShipmentStatusUpdated;
 
broadcast(new OrderShipmentStatusUpdated($update))->toOthers();

Чтобы лучше понять, когда вы можете использовать метод toOthers, представьте себе приложение для списка задач, где пользователь может создать новую задачу, введя имя задачи. Чтобы создать задачу, ваше приложение может отправить запрос по URL /task, который транслирует создание задачи и возвращает JSON-представление новой задачи. Когда ваше JavaScript-приложение получает ответ от конечной точки, оно может напрямую добавить новую задачу в свой список задач, как показано ниже:

axios.post('/task', task)
.then((response) => {
this.tasks.push(response.data);
});

Однако помните, что мы также транслируем создание задачи. Если ваше JavaScript-приложение также прослушивает это событие, чтобы добавлять задачи в список задач, у вас будут дубликаты задач в вашем списке: один от конечной точки и один от трансляции. Вы можете решить эту проблему, используя метод toOthers для указания вещателю не транслировать событие текущему пользователю.

Внимание Вашему событию необходимо использовать трейт Illuminate\Broadcasting\InteractsWithSockets, чтобы вызывать метод toOthers.

Настройка

При инициализации экземпляра Laravel Echo к соединению присваивается идентификатор сокета. Если вы используете глобальный Axios для выполнения HTTP-запросов из вашего JavaScript-приложения, идентификатор сокета автоматически добавляется ко всем исходящим запросам в виде заголовка X-Socket-ID. Затем, при вызове метода toOthers, Laravel извлечет идентификатор сокета из заголовка и укажет вещателю не транслировать событие текущему пользователю.

Если вы не используете глобальный экземпляр Axios, вам нужно будет вручную настроить ваше JavaScript-приложение для отправки заголовка X-Socket-ID со всеми исходящими запросами. Вы можете получить идентификатор сокета, используя метод Echo.socketId:

var socketId = Echo.socketId();

Настройка соединения

Если ваше приложение взаимодействует с несколькими соединениями трансляции и вы хотите транслировать событие, используя вещатель, отличный от вашего по умолчанию, вы можете указать, к которому соединению отправить событие, используя метод via:

use App\Events\OrderShipmentStatusUpdated;
 
broadcast(new OrderShipmentStatusUpdated($update))->via('pusher');

В качестве альтернативы вы можете указать соединение для трансляции событий, вызвав метод broadcastVia в конструкторе события. Однако перед этим убедитесь, что класс события использует трейт InteractsWithBroadcasting:

<?php
 
namespace App\Events;
 
use Illuminate\Broadcasting\Channel;
use Illuminate\Broadcasting\InteractsWithBroadcasting;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Broadcasting\PresenceChannel;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Queue\SerializesModels;
 
class OrderShipmentStatusUpdated implements ShouldBroadcast
{
use InteractsWithBroadcasting;
 
/**
* Создать новый экземпляр события.
*/
public function __construct()
{
$this->broadcastVia('pusher');
}
}

Получение трансляций

Ожидание событий

После того как вы установили и создали экземпляр Laravel Echo, вы готовы начать прослушивать события, транслируемые из вашего приложения Laravel. Сначала используйте метод channel для получения экземпляра канала, затем вызовите метод listen для прослушивания указанного события:

Echo.channel(`orders.${this.order.id}`)
.listen('OrderShipmentStatusUpdated', (e) => {
console.log(e.order.name);
});

Если вы хотите прослушивать события на приватном канале, используйте метод private. Вы можете продолжать цеплять вызовы метода listen, чтобы прослушивать несколько событий на одном канале:

Echo.private(`orders.${this.order.id}`)
.listen(/* ... */)
.listen(/* ... */)
.listen(/* ... */);

Прекратить прослушивание событий

Если вы хотите прекратить прослушивание определенного события без покидания канала, вы можете использовать метод stopListening:

Echo.private(`orders.${this.order.id}`)
.stopListening('OrderShipmentStatusUpdated')

Покидание канала

Чтобы покинуть канал, вы можете вызвать метод leaveChannel на вашем экземпляре Echo:

Echo.leaveChannel(`orders.${this.order.id}`);

Если вы хотите покинуть канал, а также его связанные приватные и presence-каналы, вы можете вызвать метод leave:

Echo.leave(`orders.${this.order.id}`);

Пространства имен

Вы можете заметить, что в приведенных выше примерах мы не указывали полное пространство имен App\Events для классов событий. Это потому, что Echo автоматически предполагает, что события находятся в пространстве имен App\Events. Однако вы можете настроить корневое пространство имен, передавая параметр конфигурации namespace при создании Echo:

window.Echo = new Echo({
broadcaster: 'pusher',
// ...
namespace: 'App.Other.Namespace'
});

В качестве альтернативы вы можете добавить префикс к классам событий символом . при их подписке с помощью Echo. Это позволит вам всегда указывать полностью квалифицированное имя класса:

Echo.channel('orders')
.listen('.Namespace\\Event\\Class', (e) => {
// ...
});

Каналы присутствия

Каналы presence строятся на базе безопасности приватных каналов, предоставляя дополнительную функцию отслеживания того, кто подписан на канал. Это упрощает создание мощных совместных функций приложений, таких как уведомление пользователей о том, что другой пользователь просматривает ту же страницу, или отображение списка участников чат-комнаты.

Авторизация каналов присутствия

Все каналы presence также являются приватными каналами; следовательно, пользователи должны быть авторизованы для доступа к ним. Однако, при определении обратных вызовов авторизации для каналов presence вы не вернете true, если пользователю разрешено присоединиться к каналу. Вместо этого вы должны вернуть массив данных о пользователе.

Данные, возвращаемые обратным вызовом авторизации, будут доступны слушателям событий канала presence в вашем JavaScript-приложении. Если пользователю не разрешено присоединиться к каналу presence, вы должны вернуть false или null:

use App\Models\User;
 
Broadcast::channel('chat.{roomId}', function (User $user, int $roomId) {
if ($user->canJoinRoom($roomId)) {
return ['id' => $user->id, 'name' => $user->name];
}
});

Присоединение к каналам присутствия

Чтобы присоединиться к каналу presence, вы можете использовать метод join Echo. Метод join вернет реализацию PresenceChannel, которая, помимо метода listen, позволяет вам подписываться на события here, joining и leaving.

Echo.join(`chat.${roomId}`)
.here((users) => {
// ...
})
.joining((user) => {
console.log(user.name);
})
.leaving((user) => {
console.log(user.name);
})
.error((error) => {
console.error(error);
});

Обратный вызов here будет выполнен немедленно после успешного присоединения к каналу и получит массив, содержащий информацию о пользователе для всех других пользователей, в данный момент подписанных на канал. Метод joining будет выполнен, когда новый пользователь присоединится к каналу, а метод leaving - когда пользователь покинет канал. Метод error будет выполнен, когда конечная точка аутентификации вернет код состояния HTTP, отличный от 200, или если возникнут проблемы при разборе возвращенного JSON.

Трансляция в каналах присутствия

Каналы presence могут получать события, так же как и общедоступные или приватные каналы. Используя пример чат-комнаты, мы можем хотеть транслировать события NewMessage в канал presence комнаты. Для этого мы вернем экземпляр PresenceChannel из метода broadcastOn события:

/**
* Получить каналы, на которые должно транслироваться событие.
*
* @return array<int, \Illuminate\Broadcasting\Channel>
*/
public function broadcastOn(): array
{
return [
new PresenceChannel('chat.'.$this->message->room_id),
];
}

Как и в случае с другими событиями, вы можете использовать вспомогательную функцию broadcast и метод toOthers для исключения текущего пользователя из получения трансляции:

broadcast(new NewMessage($message));
 
broadcast(new NewMessage($message))->toOthers();

Как и в случае с другими типами событий, вы можете прослушивать события, отправленные в каналы presence, используя метод listen Echo:

Echo.join(`chat.${roomId}`)
.here(/* ... */)
.joining(/* ... */)
.leaving(/* ... */)
.listen('NewMessage', (e) => {
// ...
});

Модельная трансляция

Внимание Перед тем, как приступить к чтению следующей документации о вещании модели, мы рекомендуем ознакомиться с основными концепциями служб вещания модели Laravel, а также с тем, как вручную создавать и прослушивать события вещания.

Часто возникает необходимость транслировать события, когда создаются, обновляются или удаляются модели Eloquent. Конечно же, это легко сделать, вручную определяя пользовательские события для изменений состояния модели Eloquent и помечая эти события интерфейсом ShouldBroadcast.

Однако, если вы не используете эти события в своем приложении для каких-либо других целей, создание классов событий только с целью их трансляции может быть неудобным. Для решения этой проблемы Laravel позволяет указать, что модель Eloquent должна автоматически транслировать изменения своего состояния.

Для начала вашей модели Eloquent должен использовать трейт Illuminate\Database\Eloquent\BroadcastsEvents. Кроме того, модель должна определить метод broadcastOn, который будет возвращать массив каналов, на которых события модели должны быть транслированы:

<?php
 
namespace App\Models;
 
use Illuminate\Broadcasting\Channel;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Database\Eloquent\BroadcastsEvents;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
 
class Post extends Model
{
use BroadcastsEvents, HasFactory;
 
/**
* Получить пользователя, которому принадлежит сообщение.
*/
public function user(): BelongsTo
{
return $this->belongsTo(User::class);
}
 
/**
* Получить каналы, на которые должны транслироваться события модели.
*
* @return array<int, \Illuminate\Broadcasting\Channel|\Illuminate\Database\Eloquent\Model>
*/
public function broadcastOn(string $event): array
{
return [$this, $this->user];
}
}

Как только ваша модель включит этот трейт и определит каналы трансляции, она начнет автоматически транслировать события, когда создается, обновляется, удаляется, помещается в корзину или восстанавливается экземпляр модели.

Кроме того, вы, возможно, заметили, что метод broadcastOn модели получает строковый аргумент $event. Этот аргумент содержит тип события, которое произошло с моделью, и будет иметь значение created, updated, deleted, trashed или restored. Изучив значение этой переменной, вы можете определить, на какие каналы (если это так) модель должна транслировать событие для конкретного события:

/**
* Получить каналы, на которые должны транслироваться события модели.
*
* @return array<string, array<int, \Illuminate\Broadcasting\Channel|\Illuminate\Database\Eloquent\Model>>
*/
public function broadcastOn(string $event): array
{
return match ($event) {
'deleted' => [],
default => [$this, $this->user],
};
}

Настройка создания событий модельной трансляции

Иногда вам может потребоваться настроить способ создания базового события трансляции модели Laravel. Вы можете сделать это, определив метод newBroadcastableEvent в вашей модели Eloquent. Этот метод должен возвращать экземпляр Illuminate\Database\Eloquent\BroadcastableModelEventOccurred:

use Illuminate\Database\Eloquent\BroadcastableModelEventOccurred;
 
/**
* Создать новое транслируемое событие модели для модели.
*/
protected function newBroadcastableEvent(string $event): BroadcastableModelEventOccurred
{
return (new BroadcastableModelEventOccurred(
$this, $event
))->dontBroadcastToCurrentUser();
}

Конвенции модельной трансляции

Конвенции каналов

Как вы могли заметить, метод broadcastOn в приведенном выше примере модели не возвращает экземпляры Channel. Вместо этого напрямую возвращаются модели Eloquent. Если экземпляр модели Eloquent возвращается методом broadcastOn вашей модели (или содержится в массиве, возвращаемом методом), Laravel автоматически создаст приватный канал для модели, используя имя класса модели и первичный ключ в качестве имени канала.

Таким образом, модель App\Models\User с id равным 1 будет преобразована в экземпляр Illuminate\Broadcasting\PrivateChannel с именем App.Models.User.1. Конечно же, помимо возвращения экземпляров Eloquent моделей из метода broadcastOn вашей модели, вы можете возвращать полные экземпляры Channel, чтобы иметь полный контроль над именами каналов модели:

use Illuminate\Broadcasting\PrivateChannel;
 
/**
* Получить каналы, на которые должны транслироваться события модели.
*
* @return array<int, \Illuminate\Broadcasting\Channel>
*/
public function broadcastOn(string $event): array
{
return [
new PrivateChannel('user.'.$this->id)
];
}

Если вы планируете явно возвращать экземпляр канала из метода broadcastOn вашей модели, вы можете передать экземпляр модели Eloquent в конструктор канала. При этом Laravel будет использовать соглашения о канале для модели, обсуждаемые выше, чтобы преобразовать модель Eloquent в строку имени канала:

return [new Channel($this->user)];

Если вам нужно определить имя канала для модели, вы можете вызвать метод broadcastChannel для любого экземпляра модели. Например, этот метод возвращает строку App.Models.User.1 для модели App\Models\User с id равным 1:

$user->broadcastChannel()

Конвенции событий

Поскольку модельные трансляционные события не связаны с «фактическим» событием в каталоге App\Events вашего приложения, им присваивается имя и данные на основе соглашений. Соглашение Laravel заключается в том, что событие будет транслироваться с использованием имени класса модели (без пространства имен) и имени события модели, которое вызвало трансляцию.

Так, например, обновление модели App\Models\Post будет транслировать событие в ваше клиентское приложение как PostUpdated с следующей нагрузкой:

{
"model": {
"id": 1,
"title": "My first post"
...
},
...
"socket": "someSocketId",
}

Удаление модели App\Models\User будет транслировать событие с именем UserDeleted.

При необходимости вы можете определить собственное имя трансляции и данные, добавив методы broadcastAs и broadcastWith в вашу модель. Эти методы получают имя события/операции модели, которое происходит, позволяя вам настраивать имя события и данные для каждой операции модели. Если из метода broadcastAs возвращается null, Laravel будет использовать соглашения об именовании событий трансляции модели, обсуждаемые выше, при трансляции события:

/**
* Имя трансляции события модели.
*/
public function broadcastAs(string $event): string|null
{
return match ($event) {
'created' => 'post.created',
default => null,
};
}
 
/**
* Получить данные для трансляции модели.
*
* @return array<string, mixed>
*/
public function broadcastWith(string $event): array
{
return match ($event) {
'created' => ['title' => $this->title],
default => ['model' => $this],
};
}

Ожидание модельных трансляций

После добавления трейта BroadcastsEvents в вашу модель и определения метода broadcastOn для вашей модели, вы готовы начать прослушивать транслируемые события модели в вашем клиентском приложении. Прежде чем начать, вам может потребоваться ознакомиться с полной документацией по прослушиванию событий.

В первую очередь используйте метод private для получения экземпляра канала, затем вызовите метод listen для прослушивания указанного события. Обычно имя канала, переданное методу private, должно соответствовать соглашениям о трансляции модели Laravel.

После получения экземпляра канала вы можете использовать метод listen для прослушивания конкретного события. Поскольку трансляционные события модели не связаны с «фактическим» событием в каталоге App\Events вашего приложения, имя события должно быть предварено . для указания, что оно не принадлежит определенному пространству имен. У каждого трансляционного события модели есть свойство model, которое содержит все транслируемые свойства модели:

Echo.private(`App.Models.User.${this.user.id}`)
.listen('.PostUpdated', (e) => {
console.log(e.model);
});

События клиента

Примечание При использовании Pusher Channels необходимо включить опцию «Client Events» в разделе «App Settings» вашего приложения в панели инструментов, чтобы отправлять события клиенту.

Иногда вы можете захотеть транслировать событие другим подключенным клиентам, не обращаясь к вашему приложению Laravel вообще. Это может быть особенно полезно, например, для уведомлений «набора текста», когда вы хотите оповестить пользователей вашего приложения о том, что другой пользователь печатает сообщение на определенном экране.

Для трансляции клиентских событий вы можете использовать метод whisper в Echo:

Echo.private(`chat.${roomId}`)
.whisper('typing', {
name: this.user.name
});

Для прослушивания клиентских событий вы можете использовать метод listenForWhisper:

Echo.private(`chat.${roomId}`)
.listenForWhisper('typing', (e) => {
console.log(e.name);
});

Уведомления

Совместное использование трансляции событий и уведомлений позволяет вашему JavaScript-приложению получать новые уведомления по мере их появления без необходимости обновления страницы. Прежде чем начать, обязательно ознакомьтесь с документацией по использованию канала трансляции уведомлений.

После настройки уведомления для использования канала трансляции вы можете прослушивать события трансляции с помощью метода notification Echo. Помните, что имя канала должно соответствовать имени класса сущности, которая получает уведомления:

Echo.private(`App.Models.User.${userId}`)
.notification((notification) => {
console.log(notification.type);
});

В этом примере все уведомления, отправленные экземплярами App\Models\User через канал broadcast, будут получены обратным вызовом. Обратный вызов авторизации канала для канала App.Models.User.{id} включен в поставляемый по умолчанию BroadcastServiceProvider Laravel framework.