Введение
Вдобавок к поддержке отправки email Laravel поддерживает отправку уведомлений по разным каналам доставки, включая почту, SMS (через Nexmo) и Slack. Уведомления также можно сохранять в БД, чтобы выводить их в вашем веб-интерфейсе.
Обычно уведомления — это короткие информационные сообщения для пользователей о том, что в приложении что-то произошло. Например, при создании биллингового приложения вы можете посылать пользователям уведомления «Счёт оплачен» по email и SMS каналам.
Создание уведомлений
В Laravel каждое уведомление представлено одним классом (обычно хранящимся в папке app/Notifications). Если у вас в приложении нет такой папки — не беда, она будет создана для вас при выполнении Artisan-команды shmake:notification
:
shphp artisan make:notification InvoicePaid
Эта команда поместит новый класс уведомления в папку app/Notifications. Класс каждого уведомления содержит метод PHPvia()
и разное число методов создания сообщения (таких как PHPtoMail()
и PHPtoDatabase()
), которые конвертируют уведомление в сообщение, оптимизированное под определённый канал.
Отправка уведомлений
С помощью типажа Notifiable
Уведомления можно отправлять двумя способами: используя метод PHPnotify()
типажа Notifiable, или используя фасад Notification. Сначала попробуем типаж Notifiable. Этот типаж использует стандартной моделью App\User и содержит один метод, который можно использовать для отправки уведомлений — PHPnotify()
. Этот метод принимает экземпляр уведомления:
use App\Notifications\InvoicePaid;
$user->notify(new InvoicePaid($invoice));
Запомните, вы можете использовать типаж Illuminate\Notifications\Notifiable на любой из ваших моделей. Никто не принуждает вас включать его только в модель User.
С помощью фасада Notification
Или вы можете отправлять уведомления через фасад Notification. Это особенно полезно при необходимости отправки уведомления нескольким уведомляемым сущностям, таким как коллекция пользователей. Для отправки уведомлений через фасад передайте все уведомляемые сущности и экземпляр уведомления в метод PHPsend()
:
Notification::send($users, new InvoicePaid($invoice));
Указание каналов доставки
Класс каждого уведомления содержит метод PHPvia()
, который определяет, по каким каналам будет доставляться уведомление. Изначально уведомления можно отправлять через каналы mail, database, broadcast, nexmo и slack.
Если вы хотите использовать другие каналы доставки, такие как Telegram или Pusher, зайдите на созданный сообществом сайт каналов уведомлений Laravel.
Метод PHPvia()
получает экземпляр PHP$notifiable
, который будет экземпляром класса, которому посылается уведомление. Вы можете использовать PHP$notifiable
для определения того, по каким каналам должно доставляться уведомление:
/**
* Получить каналы доставки уведомления.
*
* @param mixed $notifiable
* @return array
*/
public function via($notifiable)
{
return $notifiable->prefers_sms ? ['nexmo'] : ['mail', 'database'];
}
Постановка уведомлений в очередь
Перед использованием очереди для уведомлений вам надо настроить вашу очередь и запустить обработчика.
Отправка уведомлений может занять какое-то время, особенно если каналу необходим вызов внешнего API для доставки уведомления. Для ускорения отклика вашего приложения позвольте вашим уведомлениям вставать в очередь, добавив в их классы интерфейс ShouldQueue и типаж Queueable. Эти интерфейс и типаж уже импортированы во все уведомления, создаваемые при помощи команды shmake:notification
, поэтому вы можете сразу добавить их в класс своего уведомления:
<?php
namespace App\Notifications;
use Illuminate\Bus\Queueable;
use Illuminate\Notifications\Notification;
use Illuminate\Contracts\Queue\ShouldQueue;
class InvoicePaid extends Notification implements ShouldQueue
{
use Queueable;
// ...
}
После добавления интерфейса ShouldQueue в уведомление вы можете отправлять уведомление как обычно. Laravel обнаружит в классе интерфейс ShouldQueue и автоматически поставит доставку уведомления в очередь:
$user->notify(new InvoicePaid($invoice));
Если вы хотите отложить доставку уведомления, вы можете прицепить метод PHPdelay()
к созданию экземпляра уведомления:
$when = Carbon::now()->addMinutes(10);
$user->notify((new InvoicePaid($invoice))->delay($when));
Почтовые уведомления
Форматирование почтовых сообщений
Чтобы уведомление поддерживало отправку по email, вам надо определить метод PHPtoMail()
в классе уведомления. Этот метод получает сущность PHP$notifiable
и должен возвращать экземпляр Illuminate\Notifications\Messages\MailMessage. Почтовые сообщения могут содержать строки текста, а также «вызовы действий». Давайте посмотрим на пример метода PHPtoMail()
:
/**
* Получить представление уведомления в виде письма.
*
* @param mixed $notifiable
* @return \Illuminate\Notifications\Messages\MailMessage
*/
public function toMail($notifiable)
{
$url = url('/invoice/'.$this->invoice->id);
return (new MailMessage)
->greeting('Привет!')
->line('Один из ваших счетов оплачен!')
->action('Посмотреть счёт', $url)
->line('Спасибо за использование нашего приложения!');
}
Обратите внимание, мы используем PHP$this->invoice->id
в нашем методе PHPmessage()
. Вы можете передать любые данные, необходимые вашему уведомлению для генерирования его сообщения, в конструктор уведомления.
В этом примере мы регистрируем приветствие, строку с текстом, вызов действия, а затем ещё одну строку с текстом. Эти методы, предоставляемые объектом MailMessage, позволяют легко и быстро форматировать небольшие транзакционные письма. Затем почтовый канал переведёт компоненты сообщения в приятный, удобный HTML-шаблон email с копией в виде простого текста. Вот пример письма, сгенерированного каналом mail:
При отправке почтовых уведомлений не забудьте задать значение name в файле config/app.php. Это значение будет использоваться в заголовке и подвале сообщений ваших почтовых уведомлений.
Изменение получателя
При отправке уведомлений по каналу mail система уведомлений будет автоматически искать свойство PHPemail
в уведомляемой сущности. Вы можете изменить адрес email, используемый для доставки уведомления, определив метод PHProuteNotificationForMail()
на сущности:
<?php
namespace App;
use Illuminate\Notifications\Notifiable;
use Illuminate\Foundation\Auth\User as Authenticatable;
class User extends Authenticatable
{
use Notifiable;
/**
* Направление уведомлений для почтового канала.
*
* @return string
*/
public function routeNotificationForMail()
{
return $this->email_address;
}
}
Изменение темы
По умолчанию тема письма — имя класса уведомления в формате «Title Case». Например, если класс вашего уведомления называется InvoicePaid, тема письма будет Invoice Paid. Если вы хотите указать тему письма сами, вызовите метод PHPsubject()
при создании вашего сообщения:
/**
* Получить представление уведомления в виде письма.
*
* @param mixed $notifiable
* @return \Illuminate\Notifications\Messages\MailMessage
*/
public function toMail($notifiable)
{
return (new MailMessage)
->subject('Тема уведомления')
->line('...');
}
Изменение шаблонов
Вы можете изменить HTML-шаблоны и шаблоны в виде простого текста, используемые почтовыми уведомлениями, опубликовав ресурсы пакета уведомлений. После выполнения этой команды шаблоны почтовых уведомлений будут помещены в папку resources/views/vendor/notifications:
shphp artisan vendor:publish --tag=laravel-notifications
Сообщения об ошибках
Некоторые уведомления информирую пользователей об ошибках, таких как неудачная оплата счёта. Вы можете отметить, что почтовое сообщение связано с ошибкой, вызвав метод PHPerror()
при создании сообщения. При использовании метода PHPerror()
на почтовом сообщении кнопка вызова действия будет красной, а не синей:
/**
* Получить представление уведомления в виде письма.
*
* @param mixed $notifiable
* @return \Illuminate\Notifications\Message
*/
public function toMail($notifiable)
{
return (new MailMessage)
->error()
->subject('Тема уведомления')
->line('...');
}
Уведомления для БД
Требования
Канал уведомлений database сохраняет информацию уведомлений в таблицу БД. Эта таблица будет содержать такую информацию, как тип уведомления и JSON-данные, описывающие уведомление.
Вы можете сделать запрос к таблице, чтобы вывести уведомления в пользовательском интерфейсе приложения. Но перед этим вам надо создать таблицу для хранения ваших уведомлений. Вы можете использовать команду shnotifications:table
для создания миграции с необходимой схемой таблицы:
shphp artisan notifications:table php artisan migrate
Форматирование уведомлений для БД
Чтобы уведомление поддерживало сохранение в БД, вам надо определить метод PHPtoDatabase()
или метод PHPtoArray()
в классе уведомления. Этот метод получает сущность PHP$notifiable
и должен возвращать простой PHP-массив. Возвращённый массив будет закодирован в JSON и сохранён в столбец data вашей таблицы notifications. Давайте посмотрим на пример метода PHPtoArray()
:
/**
* Получить представление уведомления в виде массива.
*
* @param mixed $notifiable
* @return array
*/
public function toArray($notifiable)
{
return [
'invoice_id' => $this->invoice->id,
'amount' => $this->invoice->amount,
];
}
PHPtoDatabase()
или PHPtoArray()
Метод PHPtoArray()
также используется каналом broadcast для определения того, какие данные вещать вашему JavaScript-клиенту. Если вы хотите иметь два разных представления в виде массива для каналов database и broadcast, вам надо определить метод PHPtoDatabase()
вместо метода PHPtoArray()
.
Обращение к уведомлениям
Когда уведомления сохранены в БД, вам нужен удобный способ для доступа к ним из вашей уведомляемой сущности. Типаж Illuminate\Notifications\Notifiable, включённый в стандартную модель Laravel App\User, содержит Eloquent-отношение notifications, которое возвращает уведомления для сущности. Для получения уведомлений вы можете обратиться к этому методу, как к любому другому Eloquent-отношению. По умолчанию уведомления будут отсортированы по отметке времени created_at:
$user = App\User::find(1);
foreach ($user->notifications as $notification) {
echo $notification->type;
}
Если вы хотите получить только «непрочитанные» уведомления, используйте отношение unreadNotifications. Эти уведомления так же будут отсортированы по отметке времени created_at:
$user = App\User::find(1);
foreach ($user->unreadNotifications as $notification) {
echo $notification->type;
}
Для доступа к вашим уведомлениям из вашего JavaScript-клиента вам надо определить контроллер уведомлений для вашего приложения, который возвращает уведомления для уведомляемой сущности, такой как текущий пользователь. Затем вы можете сделать HTTP-запрос к URI этого контроллера из вашего JavaScript-клиента.
Пометка прочитанного уведомления
Обычно надо отметить уведомление «прочитанным», когда пользователь просмотрел его. Типаж Illuminate\Notifications\Notifiable предоставляет метод PHPmarkAsRead()
, который изменяет поле read_at записи уведомления в БД:
$user = App\User::find(1);
foreach ($user->unreadNotifications as $notification) {
$notification->markAsRead();
}
Но вместо перебора всех уведомлений вы можете использовать метод PHPmarkAsRead()
прямо на коллекции уведомлений:
$user->unreadNotifications->markAsRead();
Также вы можете использовать массовый запрос на изменение, чтобы отметить все уведомления прочитанными, не получая их из БД:
$user = App\User::find(1);
$user->unreadNotifications()->update(['read_at' => Carbon::now()]);
Само собой, вы можете удалить уведомления из таблицы целиком:
$user->notifications()->delete();
Вещание уведомлений
Требования
Перед вещанием уведомлений вам надо познакомиться со службами вещания событий Laravel и настроить их. Вещание событий предоставляет способ реагировать на события серверной части Laravel в вашем JavaScript-клиенте.
Форматирование уведомлений для вещания
Канал broadcast вещает уведомления с помощью сервисов вещания событий Laravel, позволяя вашему JavaScript-клиенту получать уведомления в реальном времени. Чтобы уведомление поддерживало вещание, вам надо определить метод PHP()toBroadcast
или метод PHP()toArray
в классе уведомления. Этот метод принимает сущность PHP$notifiable
и должен возвращать простой PHP-массив. Возвращённый массив будет закодирован в JSON и будет вещаться JavaScript-клиенту. Давайте посмотрим на пример метода PHPtoArray()
:
/**
* Получить представление уведомления в виде массива.
*
* @param mixed $notifiable
* @return array
*/
public function toArray($notifiable)
{
return [
'invoice_id' => $this->invoice->id,
'amount' => $this->invoice->amount,
];
}
В добавок к указанным вами данным вещание уведомлений будет также содержать поле type, содержащее имя класса уведомления.
PHPtoBroadcast()
или PHPtoArray()
Метод PHPtoArray()
также используется каналом database для определения того, какие данные сохранять в таблицу БД. Если вы хотите иметь два разных представления в виде массива для каналов database и broadcast, вам надо определить метод PHPtoBroadcast()
вместо метода PHPtoArray()
.
Прослушивание уведомлений
Уведомления будут вещаться в приватный канал в формате {notifiable}.{id}. Например, если вы посылаете уведомление экземпляру App\User c ID равным 1, то уведомление будет вещаться в приватный канал App.User.1. При использовании Laravel Echo вы легко можете слушать уведомления на канале вспомогательным методом PHPnotification()
:
Echo.private('App.User.' + userId)
.notification((notification) => {
console.log(notification.type);
});
SMS уведомления
Требования
Отправка SMS уведомлений в Laravel обеспечивается с помощью Nexmo. Перед отправкой уведомлений через Nexmo, вам надо установить Composer-пакет nexmo/client и добавить несколько параметров в файл настроек config/services.php. Вы можете скопировать этот пример настройки:
conf'nexmo' => [ 'key' => env('NEXMO_KEY'), 'secret' => env('NEXMO_SECRET'), 'sms_from' => '15556666666', ],
Параметр sms_from — телефонный номер, с которого будут отправляться ваши SMS сообщения. Вам надо сгенерировать этот номер для своего приложения в панели управления Nexmo.
Форматирование SMS уведомлений
Чтобы уведомление поддерживало отправку по SMS, вам надо определить метод PHP()toNexmo
в классе уведомления. Этот метод принимает сущность PHP$notifiable
и должен возвращать экземпляр Illuminate\Notifications\Messages\NexmoMessage:
/**
* Получить представления уведомления в виде Nexmo / SMS.
*
* @param mixed $notifiable
* @return NexmoMessage
*/
public function toNexmo($notifiable)
{
return (new NexmoMessage)
->content('Содержимое вашего SMS сообщения');
}
Изменение номера «From»
Если вы хотите отправлять некоторые уведомления с номера, отличающегося от указанного в вашем файле config/services.php, вы можете использовать метод PHPfrom()
на экземпляре NexmoMessage:
/**
* Получить представления уведомления в виде Nexmo / SMS.
*
* @param mixed $notifiable
* @return NexmoMessage
*/
public function toNexmo($notifiable)
{
return (new NexmoMessage)
->content('Содержимое вашего SMS сообщения')
->from('15554443333');
}
Изменение получателя SMS
При отправке уведомлений по каналу nexmo система уведомлений автоматически будет искать атрибут phone_number в уведомляемой сущности. Если вы хотите изменить телефонный номер для доставки уведомления, определите метод PHProuteNotificationForNexmo()
на сущности:
<?php
namespace App;
use Illuminate\Notifications\Notifiable;
use Illuminate\Foundation\Auth\User as Authenticatable;
class User extends Authenticatable
{
use Notifiable;
/**
* Направление уведомлений для канала Nexmo.
*
* @return string
*/
public function routeNotificationForNexmo()
{
return $this->phone;
}
}
Уведомления Slack
Требования
Перед отправкой уведомлений через Slack, вы должны установить HTTP-библиотеку Guzzle через Composer:
shcomposer require guzzlehttp/guzzle
Также вам надо настроить интеграцию с «Входящим Webhook» для вашей Slack-команды. Эта интеграция предоставит вам URL, который можно использовать при направлении уведомлений Slack.
Форматирование уведомлений Slack
Чтобы уведомление поддерживало отправку в виде Slack-сообщения, вам надо определить метод PHP()toSlack
в классе уведомления. Этот метод принимает сущность PHP$notifiable
и должен возвращать экземпляр Illuminate\Notifications\Messages\SlackMessage. В Slack-сообщении может быть текстовое содержимое, а также «attachment» (вложение), которое форматирует дополнительный текст или массив полей. Давайте посмотрим на пример метода PHPtoSlack()
:
/**
* Получить представление уведомления для Slack.
*
* @param mixed $notifiable
* @return SlackMessage
*/
public function toSlack($notifiable)
{
return (new SlackMessage)
->content('Один из ваших счетов оплачен!');
}
В этом примере мы просто отправляем одну строку текста в Slack, при этом будет создано сообщение, выглядящее вот так:
Также вы можете добавлять вложения в Slack-сообщения. Вложения обеспечивают более широкие возможности форматирования, чем простые текстовые сообщения. В этом примере мы отправим уведомление об ошибке — об исключении в приложении со ссылкой для просмотра подробной информации об исключении:
/**
* Получить представление уведомления для Slack.
*
* @param mixed $notifiable
* @return SlackMessage
*/
public function toSlack($notifiable)
{
$url = url('/exceptions/'.$this->exception->id);
return (new SlackMessage)
->error()
->content('Упс! Что-то пошло не так.')
->attachment(function ($attachment) use ($url) {
$attachment->title('Исключение: файл не найден', $url)
->content('Файл [background.jpg] не найден.');
});
}
Этот пример создаст такое Slack-сообщение:
Вложения также позволяют вам указать массив данных, который будет предоставлен пользователю. Эти данные будут представлены в виде таблицы для удобства чтения:
/**
* Получить представление уведомления для Slack.
*
* @param mixed $notifiable
* @return SlackMessage
*/
public function toSlack($notifiable)
{
$url = url('/invoices/'.$this->invoice->id);
return (new SlackMessage)
->success()
->content('Один из ваших счетов оплачен!')
->attachment(function ($attachment) use ($url) {
$attachment->title('Счёт 1322', $url)
->fields([
'Title' => 'Оплата сервера',
'Amount' => '$1,234',
'Via' => 'American Express',
'Was Overdue' => ':-1:',
]);
});
}
Этот пример создаст такое Slack-сообщение:
Изменение отправителя и получателя
Вы можете использовать методы PHPfrom()
и PHPto()
для изменения отправителя и получателя. Метод PHPfrom()
принимает имя пользователя и идентификатор emoji, а метод PHPto()
принимает имя канала или пользователя:
/**
* Получить представление уведомления для Slack.
*
* @param mixed $notifiable
* @return SlackMessage
*/
public function toSlack($notifiable)
{
return (new SlackMessage)
->from('Призрак', ':ghost:')
->to('#other')
->content('Это будет отправлено #other');
}
Изменение получателя уведомлений Slack
Для направления уведомлений Slack в нужное место определите метод PHProuteNotificationForSlack()
на уведомляемой сущности. Он должен возвращать URL того webhook, которому должно быть доставлено уведомление. Webhook URL можно генерировать с помощью добавления сервиса «Входящий Webhook» в вашу Slack-команду:
<?php
namespace App;
use Illuminate\Notifications\Notifiable;
use Illuminate\Foundation\Auth\User as Authenticatable;
class User extends Authenticatable
{
use Notifiable;
/**
* Направление уведомлений для канала Slack.
*
* @return string
*/
public function routeNotificationForSlack()
{
return $this->slack_webhook_url;
}
}
События уведомлений
Когда отправляется уведомление, система уведомлений создаёт событие Illuminate\Notifications\Events\NotificationSent. Оно содержит «уведомляемую» сущность и экземпляр самого уведомления. Вы можете зарегистрировать слушателей этого события в своём EventServiceProvider:
/**
* Привязка слушателей события для приложения.
*
* @var array
*/
protected $listen = [
'Illuminate\Notifications\Events\NotificationSent' => [
'App\Listeners\LogNotification',
],
];
После регистрации слушателей в EventServiceProvider используйте Artisan-команду shevent:generate
, чтобы быстро сгенерировать классы слушателей.
В слушателе событий вы можете обращаться к свойствам события notifiable, notification и channel, чтобы узнать больше о получателе или о самом уведомлении:
/**
* Обработка события.
*
* @param NotificationSent $event
* @return void
*/
public function handle(NotificationSent $event)
{
// $event->channel
// $event->notifiable
// $event->notification
}
Другие каналы
Laravel поставляется с набором каналов для уведомлений, но вы можете написать свой собственный драйвер для доставки уведомлений по другим каналам. В Laravel это делается просто. Сначала определите класс с методом PHPsend()
. Метод должен получать два аргумента: PHP$notifiable
и PHP$notification
:
<?php
namespace App\Channels;
use Illuminate\Notifications\Notification;
class VoiceChannel
{
/**
* Отправить данное уведомление.
*
* @param mixed $notifiable
* @param \Illuminate\Notifications\Notification $notification
* @return void
*/
public function send($notifiable, Notification $notification)
{
$message = $notification->toVoice($notifiable);
// Send notification to the $notifiable instance...
}
}
После определения класса канала уведомлений вы можете просто вернуть имя класса из метода PHPvia()
любого из ваших уведомлений:
<?php
namespace App\Notifications;
use Illuminate\Bus\Queueable;
use App\Channels\VoiceChannel;
use App\Channels\Messages\VoiceMessage;
use Illuminate\Notifications\Notification;
use Illuminate\Contracts\Queue\ShouldQueue;
class InvoicePaid extends Notification
{
use Queueable;
/**
* Получить каналы уведомлений.
*
* @param mixed $notifiable
* @return array|string
*/
public function via($notifiable)
{
return [VoiceChannel::class];
}
/**
* Получить голосовое представление уведомления.
*
* @param mixed $notifiable
* @return VoiceMessage
*/
public function toVoice($notifiable)
{
// ...
}
}