pro-dev
@pro-dev

Как использовать notifier?

Привет! Я что-то запутался с разработкой уведомлений на сайте. Нашел у симфонии готовый компонент symfony/notifier. Необходимо отправлять сообщения по разным каналам, при этом подписчики сами выбирают по каким каналам им присылать сообщения, т.е. информация о подписках хранится в базе данных. В данный момент нужны уведомления по каналам: сервис, email, sms. Сервис это по сути таблица в базе данных, для вывода в списке на сервисе, например, как у Яндекс блок уведомлений или подобных систем.

Я реализовал у себя систему подписок на Разные каналы: сервис, почта, смс. Она работает и в ней всё хорошо. Пользователи подписываются на созданные типы уведомлений. Но теперь надо всё связать как-то с этим компонентом используя Event Dispatcher и Messanger от Symfony.

1. Как я понимаю — я должен опубликовать событие через EventDispathcher, например, «пользователь зарегистрирован». Это доменное событие, которое публикуется через агрегат.
// Событие
class UserRegistered extends Event
{
    public const NAME = 'user.registered';

    protected $user;

    public function __construct(User $user)
    {
        $this-> user = $user;
    }

    public function getUser()
    {
        return $this->user;
    }
}

use Symfony\Component\EventDispatcher\EventDispatcher;

$dispatcher = new EventDispatcher();

$user = new User();
$event = new UserRegistered($user);
/ Публикация
$dispatcher->dispatch($event, UserRegistered::NAME);

2. На это событие я вешаю слушателя/подписчика и в этом подписчике реализовываю отправку в Messanger.
class NotificationSubscriber implements EventSubscriberInterface
{
    private $twig;
    private $bus;

    public function __construct(Environment $twig, MessageBusInterface $bus)
    {
        $this->twig = $twig;
        $this->bus = $bus;
    }

    public static function getSubscribedEvents()
    {
        return [
            UserRegistered::NAME => 'onNotification',
        ];
    }

    public function onNotification(UserRegistered $event)
    {
        $notification = new NotificationMessage(
            UserRegistered::NAME,
            sprintf('Пользователь %s зарегистрирован', $event->getUser()->getName()),
            $this->twig->render('notification/user-created.twig')//Шаблон User Created
        );

        $this->bus->dispatch($notification);
    }
}

3. После чего мы создаём NotificationMessage, который опубликует событие в шину
class NotificationMessage
{
    private $content;
    private $type;
    private $subject;

    public function __construct(string $type, $subject, string $content)
    {
        $this->type = $type;
        $this->content = $content;
        $this->subject = $subject;
    }

    public function getType(): string
    {
        return $this->type;
    }

    public function getSubject(): string
    {
        return $this->subject;
    }

    public function getContent(): string
    {
        return $this->content;
    }
}

4. И делаем для него обработчик NotificationHandler

use App\Model\Notification\Entity\Subscriber\SubscriberRepository;
use App\Model\Notification\Entity\Subscriber\Subscriber;
use Symfony\Component\Notifier\Notification\Notification;
use Symfony\Component\Notifier\NotifierInterface;
use Symfony\Component\Notifier\Recipient\Recipient;
use Twig\Environment;

class NotificationHandler implements MessageHandlerInterface
{
    private $subscribers;
    private $notifier;
    private $texter;

    public function __construct(SubscriberRepository $subscribers, NotifierInterface $notifier, TexterInterface $texter)
    {
        $this->subscribers = $subscribers;
        $this->notifier = $notifier;
        $this->texter = $texter;
    }

    public function __invoke(NotificationMessage $message)
    {
        /** @var Subscriber[] $subscribers */
        $subscribers = $this->subscribers->getAllActiveForType($message->getType());

        foreach ($subscribers as $subscriber) {
            foreach ($subscriber->getChannels() as $channel) {
                if ($channel->isForChannel('email')) {
                    $notification = (new Notification($message->getSubject()))
                        ->content('You got a new invoice for 15 EUR.');

                    // The receiver of the Notification
                    $recipient = new Recipient(
                        $subscriber->getEmail(),
                        $subscriber->getPhone()
                    );

                    // Send the notification to the recipient
                    $this->notifier->send($notification, $recipient);
                }

                if ($channel->isForChannel('sms')) {
                    $sms = new SmsMessage(
                        $subscriber->getPhone(),
                        $message->getContent()
                    );

                    $this->texter->send($sms);
                }

                if ($channel->isForChannel('service')) {
                    $service = new ServiceMessage(
                        $recipient,
                        $message->getSubject(),
                        $message->getContent()
                    );

                    $this->servicer->send($service);

                }
            }
        }
    }
}


В итоге у меня появились такие вопросы:
  1. Нужно ли мне публиковать событие сначала в Event Dispatcher, а затем в Messanger через слушателя. Либо мне напрямую публиковать в Messanger? И в чём тогда различие Event Dispather от Messanger?
  2. Стоит ли мне реализовывать свои NotificationMessage и NotificationHandler? И так же не могу понять нужно ли разделять по каналам эти NotificationMessage сообщения, например, как в бибилиотеке EmailMessage, SmsMessabe?
  3. Здесь он получился очень странным, думаю? Или он должен быть таким? Может быть я наворотил слишком много и Symfony Notifier имеет уже всё, что мне нужно?
  4. Как мне добавить канал уведомления в базу данных, чтобы отображать это уведомление в базе?


В общем и целом я никак не могу додуматься как по событию правильно. Отправлять сообщения по разным каналам, показывать их в сервисе и как правильно публиковать эти сообщения. Подскажите, пожалуйста в этом вопросе. Как вы используете этот компонент на крупных проектах? Я что-то запутался. Если есть какие-то хорошие примеры - с радостью их изучу. Если что-то не понятно описал - скажите в комментариях, исправлюсь.
  • Вопрос задан
  • 100 просмотров
Пригласить эксперта
Ответы на вопрос 1
glaphire
@glaphire
PHP developer
Флоу
1.1. Создать listener/subscriber, словить сам событие (симфонивское или свое кастомное)
1.2. В хендлере события создать месседж с данными о нотификейшене (если нужно сохранять данные для другой обработки, то записывать в энтити, а в месседж дублировать), кидать месседж в шину
2. Создать хендлер месседжа
3. Настроить асинхронную обработку этих мессенджев в конфигах messenger
В документации есть очень похожий пример - creating-a-message-handler
В messenger есть отдельная конфигурация для retry.

Я не делала точно такую же систему, у меня были нотификейшены на другом фреймворке, а в курсе по symfonycasts о Messenger было много примеров по настройке этого дела) Думаю что стоит уточнить с заказчиком/менеджером точный флоу всех этих времен повторной отправки, хранения данных, подтверждения успешной отправки, потому что что-то может быть критически важно, а что-то второстепенно.
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы