Может войдёшь?
Черновики Написать статью Профиль

Новая система уведомлений в Laravel 5.3

  1. 1. Создаём своё первое уведомление
  2. 2. Анатомия уведомлений
  3. 3. Что такое Notifiable (Уведомляемый)?
  4. 4. Как отправлять уведомления
  5. 5. Доступные каналы передачи
    1. 5.1. Почтовый канал
    2. 5.2. Канал базы данных
    3. 5.3. Широковещательный канал (broadcast)
    4. 5.4. Канал Nexmo
    5. 5.5. Канал Slack
    6. 5.6. Очередь для уведомлений
  6. 6. Заключение

В предыдущей статье я описал новую функцию Laravel Mailable и рассказал о некоторых важных изменениях в отправке писем в Laravel. Советую ознакомиться с ней, если вы ещё не прочитали. В двух словах: предыдущий почтовый синтаксис больше не использует «классические» почтовые замыкания, вместо этого создаются «отправляемые» (Mailable) классы для каждого отдельного письма — например, отправляемый класс «WelcomeNewUser» (Приветствие нового пользователя).

В Laravel 5.3 появилась ещё одна возможность взаимодействия с пользователями: Уведомления.

Представьте какое-нибудь сообщение, которое надо отправить пользователю, и при этом не важно как именно он его получит. Уведомление о сбросе пароля, или уведомление «Есть новые непросмотренные изменения», или «Кто-то добавил вас в друзья». Для них совсем не обязательно использовать почту, они легко могут быть отправлены по SMS, или уведомлением в Slack, или всплывающим сообщением в приложении, или любым другим из множества способов.

Система уведомлений Laravel 5.3 позволяет легко настроить отдельный класс для каждого уведомления (например, «WorkoutAssigned»«назначена тренировка»), который описывает способы передачи пользователям одного и того же сообщения по разным каналам, а также, как выбрать один из этих каналов для конкретного пользователя.

Создаём своё первое уведомление

Как и всегда, мы используем Artisan-команду для создания нашего уведомления:

shphp artisan make:notification WorkoutAssigned

Команда создаст файл app/Notifications/WorkoutAssigned.php, который будет выглядеть так:

PHP
<?php

namespace App\Notifications;

use 
Illuminate\Bus\Queueable;
use 
Illuminate\Notifications\Notification;
use 
Illuminate\Contracts\Queue\ShouldQueue;
use 
Illuminate\Notifications\Messages\MailMessage;

class 
WorkoutAssigned extends Notification
{
    use 
Queueable;

    
/**
     * Создать новый экземпляр уведомления.
     *
     * @return void
     */
    
public function __construct()
    {
        
//
    
}

    
/**
     * Получить каналы доставки уведомления.
     *
     * @param  mixed  $notifiable
     * @return array
     */
    
public function via($notifiable)
    {
        return [
'mail'];
    }

    
/**
     * Получить представление уведомления в виде письма.
     *
     * @param  mixed  $notifiable
     * @return \Illuminate\Notifications\Messages\MailMessage
     */
    
public function toMail($notifiable)
    {
        return (new 
MailMessage)
                    ->
line('Знакомство с уведомлениями.')
                    ->
action('Действие уведомления''https://laravel.com')
                    ->
line('Спасибо, что пользуетесь нашим приложением!');
    }

    
/**
     * Получить представление уведомления в виде массива.
     *
     * @param  mixed  $notifiable
     * @return array
     */
    
public function toArray($notifiable)
    {
        return [
            
//
        
];
    }
}

Анатомия уведомлений

Давайте посмотрим, что тут есть. Во-первых, конструктор, где мы будем внедрять необходимые данные.

PHP
public function __construct() {}

Во-вторых, у нас есть метод PHPvia(), позволяющий задать, через какой из возможных каналов передачи будет отправлен каждый конкретный экземпляр. Верните массив с типами уведомлений, и данное уведомление будет отправлено всеми доступными способами.

PHP
public function via($notifiable)
{
    return [
'mail'];
}

Сейчас мы просто оставим один конкретный канал передачи, но поскольку это метод, вы можете программно определить используемые каналы. Например, позволив пользователям самим задавать способы доставки.

Изначально в вашем новом уведомлении показано, как можно настроить конкретный способ доставки на примере метода PHPtoMail(). Ему передаётся «notifiable» («уведомляемый»), которого мы скоро рассмотрим, и вы создаёте и возвращаете письмо.

PHP
public function toMail($notifiable)
{
    return (new 
MailMessage)
        ->
line('Знакомство с уведомлениями.')
        ->
action('Действие уведомления''https://laravel.com')
        ->
line('Спасибо, что пользуетесь нашим приложением!');
}

И наконец, метод PHPtoArray() — стандартный запасной вариант, на который будут ссылаться те каналы передачи, которые вы не настроили (например, отправка в базу данных).

PHP
public function toArray($notifiable)
{
    return [];
}

Давайте отредактируем этот класс, чтобы он больше подходил для нашего уведомления «Назначена тренировка»:

PHP
...

class 
WorkoutAssigned extends Notification
{
    use 
Queueable;

    private 
$workout;

    public function 
__construct($workout)
    {
        
$this->workout $workout;
    }

    public function 
via($notifiable)
    {
        return [
'mail'];
    }

    public function 
toMail($notifiable)
    {
        return (new 
MailMessage)
            ->
line("Вам назначена новая тренировка!")
            ->
action('Просмотреть тренировку'route('workouts.show', [$this->workout]))
            ->
line("Давайте приступим!");
    }

    public function 
toArray($notifiable)
    {
        return [
            
'workout' => $this->workout->id
        
];
    }
}

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

Что такое Notifiable (Уведомляемый)?

Пока я говорил только об уведомлении пользователей. Но технически уведомляемой может быть любая модель Eloquent. Она должна просто импортировать типаж PHPIlluminate\Notifications\Notifiable. Вам может понадобиться уведомить Группу, Команду, Список, или любую другую подходящую модель.

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

PHP
...
class 
Group
{
    use 
Notifiable;

    public function 
routeNotificationForMail()
    {
        return 
$this->owner->email;
    }
}

Конструкция выглядит так — PHProuteNotificationFor{CHANNELNAME}, и в данном случае вам надо вернуть почтовый адрес для отправки. Другие каналы передачи будут ожидать возвращения других данных от своих методов маршрутизации.

Как отправлять уведомления

Есть два пути отправки уведомлений. Первый — использование фасада PHPNotification:

PHP
Notification::send(User::first(), new WorkoutAssigned($workout));

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

PHP
Notification::send(User::all(), new DowntimePlanned($date));

Второй путь — использование метода PHPnotify() на вашей модели, которая импортирует типаж PHPNotifiable (например, стандартный класс PHPUser):

PHP
$user->notify(new WorkoutAssigned($workout));

Перед отправкой первого уведомления задайте новое свойство «name» в файле config/app.php. Это имя вашего приложения, которое будет отображаться в заголовке и в подвале ваших писем.

Вот как выглядит почтовое уведомление по умолчанию для нашего класса Workout Assigned:

/packages/proger/habravel/uploads/689-default-email-notification.png

Доступные каналы передачи

Какие ещё доступны каналы, кроме PHPmail? Стандартные каналы: PHPdatabase, PHPbroadcast, PHPnexmo и PHPslack, но вы можете найти и другие на сайте каналов уведомлений Laravel, созданном сообществом.

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

PHP
public function via($notifiable)
{
    return 
$notifiable->prefers_sms ? ['nexmo'] : ['mail''database'];
}

Также вы можете встроить эту логику в саму модель:

PHP
// в уведомлении
public function via($notifiable)
{
    return 
$notifiable->preferredNotificationChannel();
}

// в классе User
public function preferredNotificationChannel()
{
    return 
PresenceChecker::isOnline($this) ? ['broadcast'] : ['mail'];
}

Почтовый канал

Мы уже рассмотрели основы отправки уведомлений по почте, но есть ещё множество доступных настроек.

Вы можете настроить тему письма (которая по умолчанию берётся из имени класса уведомления, например, для класса «WorkoutAssigned» тема будет «Workout Assigned») с помощью метода PHPsubject():

PHP
public function toMail($notifiable)
{
    return (new 
MailMessage)
        ->
subject('Вам назначена новая тренировка!')
    ...
}

Вы можете настроить приветствие (по умолчанию «Hello!») с помощью метода PHPgreeting():

PHP
public function toMail($notifiable)
{
    return (new 
MailMessage)
        ->
greeting("За делоооооооо!")
    ...
}

Используйте шаблон «error», чтобы изменить цвет с синего на красный:

PHP
public function toMail($notifiable)
{
    return (new 
MailMessage)
        ->
error()
    ...
}

И наконец, вы можете опубликовать и настроить шаблон для писем:

shphp artisan vendor:publish --tag=laravel-notifications

Теперь текстовые и HTML шаблоны будут находиться в resources/views/vendor/notifications.

Канал базы данных

Канал отправки уведомлений в базу данных сохраняет каждое уведомление в таблице БД, а вы можете обработать их в приложении, как пожелаете.

Вы можете создать миграцию для этой таблицы командой shphp artisan notifications:table.

Если вы не определите метод PHPtoDatabase() для вашего уведомления, то Laravel использует метод PHPtoArray() для определения данных, которые необходимо поместить в БД. Но вы можете настроить и это. Чтобы вы ни вернули из методов PHPtoDatabase() и PHPtoArray(), в столбце data базы данных оно будет в формате JSON.

PHP
// в уведомлении
public function toDatabase($notifiable)
{
    return [
        
'trainee_id' => $notifiable->id,
        
'workout_id' => $this->workout->id
    
];
}

Вы легко можете получить эти уведомления через отношение PHPnotifications(), которое добавляется в вашу модель типажом PHPNotifiable. Благодаря этому появляются дополнительные возможности работы с «прочитанными» и «непрочитанными» уведомлениями. У каждого уведомления есть метод PHPmarkAsRead(), который используется для изменения свойства read_at. А для получения всех «непрочитанных» уведомлений используется метод PHPunreadNotifications() на модели:

PHP
foreach ($user->notifications as $notification) {
    
// произвести действия

    
$notification->markAsRead();
}

// позже...

foreach ($user->unreadNotifications as $notification) {
    
// новые!
}

Широковещательный канал (broadcast)

Если вы не знакомы с вещанием событий Laravel, то для понимания широковещательного канала вы можете ознакомиться с моей статьёй о вещании событий Laravel.

Широковещательный канал рассылает события с данными из ваших уведомлений в ваш Websocket-клиент. Для этих уведомлений он использует закрытый канал {notifiableClassNameDotNotated}.{id}, т.е. при уведомлении пользователя 15 вещание будет происходить в закрытый канал App.User.15.

Как и другие методы, вещание уведомлений по умолчанию берёт структуру данных из PHPtoArray(), если вы не определите метод PHPtoBroadcast().

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

PHP
var userId 15// задать в каком-либо месте

Echo.private('App.User.' userId)
    .
notification((notification) => {
        
console.log(notification);
    });

Канал Nexmo

С помощью канала Nexmo можно легко посылать уведомления по SMS. Вам надо указать ваши учётные данные Nexmo в config/services.php в ключе nexmo вот так:

PHP
'nexmo' => [
    
'key' => env('NEXMO_KEY'),
    
'secret' => env('NEXMO_SECRET'),
    
'sms_from' => '15558675309',
],

И определить метод PHPtoNexmo(), который возвращает экземпляр PHPIlluminate\Notifications\Message\NexmoMessage:

PHP
public function toNexmo($notifiable)
{
    return (new 
NexmoMessage)
        ->
content('Смотри, это у тебя в телефоне!');
}

Если почтовый канал проверяет свойство email уведомляемого, то канал Nexmo проверяет свойство phone_number для отправки сообщения. Это можно настроить методом PHProuteNotificationForNexmo():

PHP
// в уведомлении
public function routeNotificationForNexmo()
{
    return 
$this->sms_number;
}

Канал Slack

Канал Slack вещает ваши уведомления в Slack-чат.

Чтобы использовать уведомления Slack, вам необходимо установить Guzzle через Composer: shcomposer require guzzlehttp/guzzle.

Сначала зайдите в свой аккаунт Slack, раздел «Apps and Integrations» (Приложения и Интеграция xmlhttps://{yourteam}.slack.com/apps). Выберите тип «Incoming Webhook» (Входящий Webhook) и добавьте новую конфигурацию. Вы можете указать чат для отправки и другие настройки.

/packages/proger/habravel/uploads/689-slack-integration-config-screen.png

Скопируйте Webhook URL и вернитесь к своему Laravel-приложению.

Ваш уведомляемый должен иметь метод PHProuteNotificationForSlack() , который должен возвращать этот Webhook URL:

PHP
public function routeNotificationForSlack()
{
    return 
$this->slack_webhook_url;
}

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

PHP
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:',
                ]);
        });
}
/packages/proger/habravel/uploads/689-complex-slack-notification-example.png

Также вы можете оставить его максимально простым, просто сгенерируйте PHPSlackMessage и определите только его свойство PHPcontent:

PHP
public function toSlack($notifiable)
{
    return (new 
SlackMessage)
        ->
content('Один из ваших счетов оплачен!');
}

Очередь для уведомлений

Все уведомления, реализующие интерфейс PHPShouldQueue и импортирующие типаж PHPQueueable, будут помещаться в вашу очередь и отправляться асинхронно. Поскольку большинство каналов требуют отправки HTTP-запросов, то крайне рекомендуется использовать очередь для уведомлений.

Заключение

Вот и всё!

Это великолепно. Уведомления так просты и надёжны, что вам не придётся больше искать какие-то другие инструменты для этого (почту, Slack SDK напрямую и т.п.), особенно когда вы увидите, сколько каналов создано сообществом. Сумасшедшие люди.

Но как и всегда, с большими возможностями приходит большая ответственность. Будьте аккуратны в обращении с пользователями, не переборщите с уведомлениями.

Ну что ж, в бой. Уведомляйте!

Как вы считаете, полезен ли этот материал? Да Нет

Написать комментарий

Разметка: ? ?

Авторизуйся, чтобы прокомментировать.