Створення та відправлення сповіщень

Дата оновлення перекладу 2024-06-10

Створення та відправлення сповіщень

Установка

Сучасні веб-додатки використовують багато каналів для відправлення повідомлень користувачам (такі як SMS, повідомлення в Slack, email, push-повідомлення та ін.). Компонент Symfony Notifier - це абстракція над усіма цими каналами. Він надає динамічний спосіб керування тим, як відправляються повідомлення. Встановіть Notifier за допомогою:

1
$ composer require symfony/notifier

Канали: Chatters, Texters, Email, Browser і Push

Компонент Notifier може відправляти сповіщення різними каналами. Кожний канал може за допомогою транспортів інтегруватися з різними постачальниками (наприклад, Slack або Twilio SMS).

Компонент Notifier підтримує такі канали:

  • SMS-канал відправляє сповіщення на телефони SMS-повідомленнями;
  • Чат-канал відправляє сповіщення у чат-сервіси, такі як Slack і Telegram;
  • Email-канал інтегрується з Symfony Mailer;
  • Браузер-канал використовує флеш-повідомлення .
  • Push-канал відправляє сповіщення на телефони та браузери через пуш-сповіщення.

Tip

Використовуйте секрети для безпечного зберігання ваших API-токенів.

SMS-канал

Caution

Якщо будь-яке зі значень DSN містить будь-який символ, який вважається спеціальним в URI (на кшталт, as +, @, $, #, /, :, *, !), ви повинні зашифрувати їх. Див. RFC 3986, щоб побачити повний перелік зарезервованих символів, або використайте функцію urlencode, щоб зашифрувати їх.

SMS-канал використовує класи Texter для відправлення SMS-повідомлень на мобільні телефони. Ця функція вимагає підписки на зовнішні сервіси, які відправляють SMS-повідомлення. Symfony надає інтеграцію з декількома популярними SMS-сервісами:

Tip

Деякі сторонні транспорти, при використанні API, підтримують зворотні виклики статусу через веб-хуки. Дивіться Документацію про веб-хуки для отримання додаткової інформації.

7.1

Інтеграції SmsSluzba та SMSense були представлені вSymfony 7.1.

7.1

Інтеграція Sms77 застаріла, починаючи з Symfony 7.1, натомість використовуйте інтеграцію Seven.io.

Для ввімкнення texter, додайте коректний DSN у ваш файл .env та налаштуйте texter_transports:

1
2
# .env
TWILIO_DSN=twilio://SID:TOKEN@default?from=FROM
1
2
3
4
5
# config/packages/notifier.yaml
framework:
    notifier:
        texter_transports:
            twilio: '%env(TWILIO_DSN)%'

Клас TexterInterface дозволяє вам відправляти SMS-повідомлення:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// src/Controller/SecurityController.php
namespace App\Controller;

use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Notifier\Message\SmsMessage;
use Symfony\Component\Notifier\TexterInterface;
use Symfony\Component\Routing\Attribute\Route;

class SecurityController
{
    #[Route('/login/success')]
    public function loginSuccess(TexterInterface $texter): Response
    {
        $options = (new ProviderOptions())
            ->setPriority('high')
        ;

        $sms = new SmsMessage(
            // номер телефону, на який потрібно відправити SMS
            '+1411111111',
            // повідомлення
            'A new login was detected!',
            // опціонально, ви можете перевизначити "from", визначене за замовчуванням у транспорті
            '+1422222222',
        );

        $sentMessage = $texter->send($sms);

        // ...
    }
}

Метод send() повертає змінну типу SentMessage, яка надає інформацію на кшталт ID повідомлення та оригінального змісту повідомлення.

Чат-канал

Caution

Якщо будь-яке зі значень DSN містить будь-який символ, який вважається спеціальним в URI (на кшталт, as +, @, $, #, /, :, *, !), ви повинні зашифрувати їх. Див. RFC 3986, щоб побачити повний перелік зарезервованих символів, або використайте функцію urlencode, щоб зашифрувати їх.

Канал чатів використовується для відправлення повідомлень користувачам у чати, використовуючи класи Chatter. Symfony надає інтеграцію з цими сервісами чатів:

?????? ????? DSN
AmazonSns symfony/amazon-sns-notifier sns://ACCESS_KEY:SECRET_KEY@default?region=REGION
Bluesky symfony/bluesky-notifier bluesky://USERNAME:PASSWORD@default
Chatwork symfony/chatwork-notifier chatwork://API_TOKEN@default?room_id=ID
Discord symfony/discord-notifier discord://TOKEN@default?webhook_id=ID
FakeChat symfony/fake-chat-notifier fakechat+email://default?to=TO&from=FROM or fakechat+logger://default
Firebase symfony/firebase-notifier firebase://USERNAME:PASSWORD@default
Gitter symfony/gitter-notifier gitter://TOKEN@default?room_id=ROOM_ID
GoogleChat symfony/google-chat-notifier googlechat://ACCESS_KEY:ACCESS_TOKEN@default/SPACE?thread_key=THREAD_KEY
LINE Notify symfony/line-notify-notifier linenotify://TOKEN@default
LinkedIn symfony/linked-in-notifier linkedin://TOKEN:USER_ID@default
Mastodon symfony/mastodon-notifier mastodon://ACCESS_TOKEN@HOST
Mattermost symfony/mattermost-notifier mattermost://ACCESS_TOKEN@HOST/PATH?channel=CHANNEL
Mercure symfony/mercure-notifier mercure://HUB_ID?topic=TOPIC
MicrosoftTeams symfony/microsoft-teams-notifier microsoftteams://default/PATH
RocketChat symfony/rocket-chat-notifier rocketchat://TOKEN@ENDPOINT?channel=CHANNEL
Slack symfony/slack-notifier slack://TOKEN@default?channel=CHANNEL
Telegram symfony/telegram-notifier telegram://TOKEN@default?channel=CHAT_ID
Twitter symfony/twitter-notifier twitter://API_KEY:API_SECRET:ACCESS_TOKEN:ACCESS_SECRET@default
Zendesk symfony/zendesk-notifier zendesk://EMAIL:TOKEN@SUBDOMAIN
Zulip symfony/zulip-notifier zulip://EMAIL:TOKEN@HOST?channel=CHANNEL

7.1

Інтеграції Bluesky, Unifonic та Smsbox були представлені в Symfony 7.1.

Caution

By default, if you have the Messenger component installed, the notifications will be sent through the MessageBus. If you don't have a message consumer running, messages will never be sent.

To change this behavior, add the following configuration to send messages directly via the transport:

1
2
3
4
# config/packages/notifier.yaml
framework:
    notifier:
        message_bus: false

Chatters конфігуруються за допомогою налаштування chatter_transports:

1
2
# .env
SLACK_DSN=slack://TOKEN@default?channel=CHANNEL
1
2
3
4
5
# config/packages/notifier.yaml
framework:
    notifier:
        chatter_transports:
            slack: '%env(SLACK_DSN)%'

Клас ChatterInterface дозволяє вам відправляти повідомлення у чат-сервісах:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// src/Controller/CheckoutController.php
namespace App\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Notifier\ChatterInterface;
use Symfony\Component\Notifier\Message\ChatMessage;
use Symfony\Component\Routing\Attribute\Route;

class CheckoutController extends AbstractController
{
    #[Route('/checkout/thankyou')]
    public function thankyou(ChatterInterface $chatter): Response
    {
        $message = (new ChatMessage('You got a new invoice for 15 EUR.'))
            // якщо не встановлено чітко, повідомлення відправляється
            // транспорту за замовчуванням (першому сконфігурованому)
            ->transport('slack');

        $sentMessage = $chatter->send($message);

        // ...
    }
}

Метод send() повертає змінну типу SentMessage, яка надає інформацію на кшталт ID повідомлення та оригінального змісту повідомлення.

Email-канал

Email-канал використовує Symfony Mailer для відправлення сповіщень, використовуючи спеціальний NotificationEmail. Необхідно встановити Twig bridge разом з Inky та Twig розширенням CSS Inliner:

1
$ composer require symfony/twig-pack twig/cssinliner-extra twig/inky-extra

Після цього налаштуйте mailer . Ви також можете налаштувати адресу email з якої будуть приходити листи-сповіщення:

1
2
3
4
5
6
# config/packages/mailer.yaml
framework:
    mailer:
        dsn: '%env(MAILER_DSN)%'
        envelope:
            sender: 'notifications@example.com'

Push-канал

Caution

Якщо будь-яке зі значень DSN містить будь-який символ, який вважається спеціальним в URI (на кшталт, as +, @, $, #, /, :, *, !), ви повинні зашифрувати їх. Див. RFC 3986, щоб побачити повний перелік зарезервованих символів, або використайте функцію urlencode, щоб зашифрувати їх.

Push-канал використовується для відправлення користувачам сповіщень з використанням класів Texter. Symfony надає інтеграцію з цими пуш-сервісами:

?????? ????? DSN
Engagespot symfony/engagespot-notifier engagespot://API_KEY@default?campaign_name=CAMPAIGN_NAME
Expo symfony/expo-notifier expo://Token@default
Novu symfony/novu-notifier novu://API_KEY@default
Ntfy symfony/ntfy-notifier ntfy://default/TOPIC
OneSignal symfony/one-signal-notifier onesignal://APP_ID:API_KEY@default?defaultRecipientId=DEFAULT_RECIPIENT_ID
PagerDuty symfony/pager-duty-notifier pagerduty://TOKEN@SUBDOMAIN
Pushover symfony/pushover-notifier pushover://USER_KEY:APP_TOKEN@default
Pushy symfony/pushy-notifier pushy://API_KEY@default

Щоб увімкнути texter, додайте правильну DSN у ваш файл .env, та сконфігуруйте texter_transports:

7.1

Інтеграція Pushy була представлена в Symfony 7.1.

1
2
# .env
EXPO_DSN=expo://TOKEN@default
1
2
3
4
5
# config/packages/notifier.yaml
framework:
    notifier:
        texter_transports:
            expo: '%env(EXPO_DSN)%'

Конфігурація резервного транспорту або балансування Round-Robin

Окрім налаштування одного або декількох окремих транспортів, ви також можете викоритовувати спеціальні символи || і && для реалізації резервного або round-robin транспорту:

1
2
3
4
5
6
7
8
9
10
# config/packages/notifier.yaml
framework:
    notifier:
        chatter_transports:
            # Відправляти сповіщення в Slack та використовувати Telegram, якщо
            # Slack видав помилку
            main: '%env(SLACK_DSN)% || %env(TELEGRAM_DSN)%'

            # Відправляти сповіщення використовуючи наступний запланований транспорт, розрахований методом round robin
            roundrobin: '%env(SLACK_DSN)% && %env(TELEGRAM_DSN)%'

Створення та відправлення сповіщень

Для відправлення сповіщення, автоматично підключіть NotifierInterface (ID сервісу - notifier). У цього класу є метод send(), який дозволяє вам відправляти сповіщення Notification отримувачу Recipient:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// src/Controller/InvoiceController.php
namespace App\Controller;

use Symfony\Component\Notifier\Notification\Notification;
use Symfony\Component\Notifier\NotifierInterface;
use Symfony\Component\Notifier\Recipient\Recipient;

class InvoiceController extends AbstractController
{
    #[Route('/invoice/create')]
    public function create(NotifierInterface $notifier): Response
    {
        // ...

        // Створити сповіщення, яке відправиться
        // через "email" канал
        $notification = (new Notification('New Invoice', ['email']))
            ->content('You got a new invoice for 15 EUR.');

        // Отримувач сповіщення
        $recipient = new Recipient(
            $user->getEmail(),
            $user->getPhonenumber()
        );

        // Відправити сповіщення отримувачу
        $notifier->send($notification, $recipient);

        // ...
    }
}

Сповіщення Notification створюється з 2 аргументами: тема та канали. Канали вказують, який канал або транспорт має використовуватися для відправлення сповіщення. Наприклад, ['email', 'sms'] відправлять і email, і sms-сповіщення користувачу.

Сповіщення за замовчуванням також має методи content() і emoji(), щоб встановлювати зміст та іконку змісту.

Symfony надає таких отримувачів:

NoRecipient
Отримувач за замовчуванням, який корисний, коли не треба мати інформацію про отримувача. Наприклад, браузер-канал використовує flashbag сесії поточного запиту;
Recipient
Може містити і email, і телефон користувача. Цей отримувач може використовуватися для всіх каналів (залежить від того, чи дійсно вони встановлені).

Конфігурація політики каналів

Замість вказування цільових каналів при створенні, Symfony також дозволяє вам використовувати рівні важливості сповіщень. Оновіть конфігурацію для вказання, які канали мають використовуватися для потрібних рівнів (використовуючи channel_policy):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# config/packages/notifier.yaml
framework:
    notifier:
        # ...
        channel_policy:
            # Використовувати SMS, Slack і email для термінових сповіщень
            urgent: ['sms', 'chat/slack', 'email']

            # Використовувати Slack для важливих сповіщень
            high: ['chat/slack']

            # Використовувати браузер для сповіщень середнього та низкього рівнів
            medium: ['browser']
            low: ['browser']

Тепер, коли важливість сповіщення встановлена як "high", воно буде відправлене через транспорт Slack:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// ...
class InvoiceController extends AbstractController
{
    #[Route('/invoice/create')]
    public function invoice(NotifierInterface $notifier): Response
    {
        // ...

        $notification = (new Notification('New Invoice'))
            ->content('You got a new invoice for 15 EUR.')
            ->importance(Notification::IMPORTANCE_HIGH);

        $notifier->send($notification, new Recipient('wouter@example.com'));

        // ...
    }
}

Користувацьке налаштування сповіщень

Ви можете розширити базові класи Notification або Recipient для налаштування їх поведінки. Наприклад, ви можете перевизначити метод getChannels(), щоб повертати sms лише якщо сума рахунку дуже велика і в отримувача є телефонний номер:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
namespace App\Notifier;

use Symfony\Component\Notifier\Notification\Notification;
use Symfony\Component\Notifier\Recipient\RecipientInterface;
use Symfony\Component\Notifier\Recipient\SmsRecipientInterface;

class InvoiceNotification extends Notification
{
    public function __construct(
        private int $price,
    ) {
    }

    public function getChannels(RecipientInterface $recipient): array
    {
        if (
            $this->price > 10000
            && $recipient instanceof SmsRecipientInterface
        ) {
            return ['sms'];
        }

        return ['email'];
    }
}

Користувацьке налаштування повідомлень сповіщень

У кожного каналу є власний інтерфейс сповіщень, який ви можете реалізувати для налаштування повідомлень сповіщень. Наприклад, якщо ви хочете змінити повідомлення на основі чат-сервісу, реалізуйте ChatNotificationInterface та його метод asChatMessage():

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// src/Notifier/InvoiceNotification.php
namespace App\Notifier;

use Symfony\Component\Notifier\Message\ChatMessage;
use Symfony\Component\Notifier\Notification\ChatNotificationInterface;
use Symfony\Component\Notifier\Notification\Notification;
use Symfony\Component\Notifier\Recipient\RecipientInterface;

class InvoiceNotification extends Notification implements ChatNotificationInterface
{
    public function __construct(
        private int $price,
    ) {
    }

    public function asChatMessage(RecipientInterface $recipient, string $transport = null): ?ChatMessage
    {
        // Додайте свій emoji, якщо повідомлення надсилається в Slack
        if ('slack' === $transport) {
            return (new ChatMessage('You\'re invoiced '.$this->price.' EUR.'))
                ->emoji('money');
        }

        // Якщо ви повернете null, Notifier створить ChatMessage
        // так, якщо б цього методу не було
        return null;
    }
}

Також є інтерфейси SmsNotificationInterface, EmailNotificationInterface та EmailNotificationInterface для зміни повідомлень, що надсилаються по цих каналах.

Користувацьке налаштування сповіщень браузера (флеш-повідомлення)

Поведінка сповіщень каналу браузера за замовчуванням - додавати флеш-повідомлення з notification в якості ключа.

Однак, ви можете захотіти вказати рівень важливості сповіщення типу флеш-повідомлення, щоб ви могли підлаштувати їх стиль.

Ви можете зробити це, перевизначивши сервіс за замовчуванням notifier.flash_message_importance_mapper на вашу власну реалізацію FlashMessageImportanceMapperInterface, де ви можете надати вашу власну "важливість" як "рівень небезпеки".

На даний момент Symfony надає реалізацію для типових рівнів небезпеки для CSS фреймворку Bootstrap, які ви можете реалізувати негайно, використавши:

1
2
3
4
# config/services.yaml
services:
    notifier.flash_message_importance_mapper:
        class: Symfony\Component\Notifier\FlashMessage\BootstrapFlashMessageImportanceMapper

Тестування Notifier

Symfony надає NotificationAssertionsTrait, які надають корисні методи для тестування вашої реалізації Notifier. Ви можете отримати переваги цього класу, використовуючи його напряму, або розширивши KernelTestCase.

Див. документацію тестування , щоб побачити список доступних тверджень.

Відключення доставки

Під час розробки та тестування ви можете захотіти повністю відключити відправлення сповіщень. Ви можете зробити це, надавши Notifier транспорт NullTransport для всіх налаштованих транспортів texter та chatter, але лише для dev (та/або test) середовищ:

1
2
3
4
5
6
7
# config/packages/dev/notifier.yaml
framework:
    notifier:
        texter_transports:
            twilio: 'null://null'
        chatter_transports:
            slack: 'null://null'

Використання подій

Клас Transport` компонента Notifier дозволяє вам опціонально підключатися до життєвого циклу через події.

Подія MessageEvent::class

Типові цілі: Зробити щось до відправки повідомлення (на кшталт логування того, яке повідомлення буде відправлено або відображення чогось про подію, яка буде виконана).

Прямо перед відправкою повідомлення, оголошується клас події MessageEvent. Слухачі отримують подію MessageEvent:

1
2
3
4
5
6
7
8
9
use Symfony\Component\Notifier\Event\MessageEvent;

$dispatcher->addListener(MessageEvent::class, function (MessageEvent $event) {
    // отримує екземпляр повідомлення
    $message = $event->getMessage();

    // логувати щось
    $this->logger(sprintf('Message with subject: %s will be send to %s, $message->getSubject(), $message->getRecipientId()'));
});

Подія FailedMessageEvent

Типові цілі: Зробити щось перед тим, як буде викликано виключення (повторно спробувати відправити повідомлення або записати лог додаткової інформації).

Кожен раз, коли викликається виключення під час відправки повідомлення, оголошується клас події FailedMessageEvent. Слухач може зробити щось корисне до того, як буде викликане виключення.

Слухачі отримують подію FailedMessageEvent:

1
2
3
4
5
6
7
8
9
10
11
12
use Symfony\Component\Notifier\Event\FailedMessageEvent;

$dispatcher->addListener(FailedMessageEvent::class, function (FailedMessageEvent $event) {
    // отримує екземпляр повідомлення
    $message = $event->getMessage();

    // отримує екземпляр помилки
    $error = $event->getError();

    // логувати щось
    $this->logger(sprintf('The message with subject: %s has not been sent successfully. The error is: %s, $message->getSubject(), $error->getMessage()'));
});

Подія SentMessageEvent

Типові цілі: Виконати якусь дію, якщо повідомлення було відправлено успішно
(на кшталт отримання id, поверненого при відправці повідомлення).

Після того, як повідомлення було успішно відправено, оголошується клас події SentMessageEvent. Слухачі отримують подію SentMessageEvent:

1
2
3
4
5
6
7
8
9
use Symfony\Component\Notifier\Event\SentMessageEvent;

$dispatcher->addListener(SentMessageEvent::class, function (SentMessageEvent $event) {
    // отримує екземпляр повідомлення
    $message = $event->getOriginalMessage();

    // логувати щось
    $this->logger(sprintf('The message has been successfully sent and has id: %s, $message->getMessageId()'));
});