Урок 12: События (Events) и слушатели в Symfony

В 12-ом уроке мы разберем одну из возможностей Symfony, это работу с событиями. Этот урок станет важным шагом в понимании гибкости и расширяемости фреймворка. Мы научимся создавать кастомные события, настраивать слушателей и подписчиков, а также реализуем практический пример отправки email после регистрации пользователя.

Что такое EventDispatcher?

В Symfony за обработку событий отвечает компонент EventDispatcher. Это «посредник», который связывает события (например, «пользователь зарегистрировался») с их обработчиками (например, «отправить email»).

Его ключевые задачи:

  1. Декомпозиция кода. Разделение логики на независимые части.
  2. Гибкость. Можно добавлять или удалять обработчики без изменения основного кода.
  3. Стандартизация. Единый способ реагировать на действия в приложении.

Как это работает?

  1. Событие (Event) генерируется в определенный момент (например, после сохранения пользователя).
  2. EventDispatcher получает событие и ищет все связанные с ним слушатели (Listeners) или подписчики (Subscribers).
  3. Слушатели выполняют свою логику в ответ на событие.

Создание кастомных событий

Давай создадим свое событие. Предположим, мы хотим отправлять email после регистрации пользователя.

Шаг 1: Создаем класс события

Кастомное событие это обычный PHP-класс, который хранит данные, связанные с событием. Создадим UserRegisteredEvent:

php
// src/Event/UserRegisteredEvent.php
namespace App\Event;

use Symfony\Contracts\EventDispatcher\Event;
use App\Entity\User;

class UserRegisteredEvent extends Event
{
    public const NAME = 'user.registered';

    private $user;

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

    public function getUser(): User
    {
        return $this->user;
    }
}
  • NAME — уникальное имя события.
  • Конструктор принимает объект пользователя, который будет передан слушателям.

Шаг 2: Генерируем событие

Теперь вызовем событие после регистрации пользователя. Например в контроллере:

php
// src/Controller/RegistrationController.php
use App\Event\UserRegisteredEvent;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;

class RegistrationController extends AbstractController
{
    public function register(
        Request $request, 
        EventDispatcherInterface $dispatcher
    ): Response {
        // ... логика регистрации
        
        // Создаем и диспатчим событие
        $event = new UserRegisteredEvent($user);
        $dispatcher->dispatch($event, UserRegisteredEvent::NAME);
        
        return $this->redirectToRoute('home');
    }
}

Метод dispatch() принимает объект события и его имя (опционально, но рекомендуется для ясности).

Слушатели (Listeners) или Подписчики (Subscribers)

В Symfony есть два подхода к обработке событий. Разберем их различия.

Слушатели (Listeners)

Слушатель это PHP-класс или функция, которая реагирует на конкретное событие.
Пример слушателя для отправки email:

php
// src/EventListener/SendWelcomeEmailListener.php
namespace App\EventListener;

use App\Event\UserRegisteredEvent;
use Symfony\Component\Mailer\MailerInterface;

class SendWelcomeEmailListener
{
    private $mailer;

    public function __construct(MailerInterface $mailer)
    {
        $this->mailer = $mailer;
    }

    public function onUserRegistered(UserRegisteredEvent $event): void
    {
        $user = $event->getUser();
        // ... логика отправки email
    }
}

Регистрируем слушатель в config/services.yaml:

yaml
services:
    App\EventListener\SendWelcomeEmailListener:
        tags:
            - { name: kernel.event_listener, event: user.registered, method: onUserRegistered }

Подписчики (Subscribers)

Подписчик это класс, который реализует интерфейс EventSubscriberInterface и определяет, на какие события он подписан.
Пример подписчика для логирования:

php
// src/EventSubscriber/LogActivitySubscriber.php
namespace App\EventSubscriber;

use App\Event\UserRegisteredEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;

class LogActivitySubscriber implements EventSubscriberInterface
{
    public static function getSubscribedEvents(): array
    {
        return [
            UserRegisteredEvent::NAME => 'logUserRegistration',
        ];
    }

    public function logUserRegistration(UserRegisteredEvent $event): void
    {
        $user = $event->getUser();
        // ... логика логирования
    }
}

Symfony автоматически обнаружит подписчик благодаря интерфейсу.

В чем разница?

Критерий Слушатели Подписчики
Регистрация Через конфиг или атрибуты Через интерфейс EventSubscriberInterface
Гибкость Подходит для простых сценариев Удобен для группировки связанных событий
Сложность Проще Требует реализации интерфейса

Пример отправки email после регистрации

Давай реализуем полноценный пример с отправкой приветственного письма.

Шаг 1: Настраиваем Mailer

Убедись, что в .env указаны настройки почты:

env
MAILER_DSN=smtp://user:pass@smtp.example.com:port

Шаг 2: Дополняем слушатель

Модифицируем SendWelcomeEmailListener:

php
// src/EventListener/SendWelcomeEmailListener.php
use Symfony\Bridge\Twig\Mime\TemplatedEmail;

public function onUserRegistered(UserRegisteredEvent $event): void
{
    $user = $event->getUser();
    $email = (new TemplatedEmail())
        ->to($user->getEmail())
        ->subject('Добро пожаловать!')
        ->htmlTemplate('emails/welcome.html.twig')
        ->context([
            'user' => $user,
        ]);

    $this->mailer->send($email);
}

Шаг 3: Создаем шаблон письма

templates/emails/welcome.html.twig:

twig
<h1>Привет, {{ user.username }}!</h1>
<p>Спасибо за регистрацию на нашем сайте.</p>

Практические задания

Закрепим материал задачами.

Задача 1: Создайте событие «Комментарий добавлен»

  1. Создайте класс CommentCreatedEvent.
  2. Генерируйте событие после сохранения комментария в базе.
  3. Напишите слушатель, который отправляет уведомление администратору.

Задача 2: Реализуйте подписчик для логирования

  1. Создайте подписчик LoggingSubscriber.
  2. Подпишитесь на события kernel.exception и user.registered.
  3. Логируйте информацию о событиях в файл var/log/events.log.

Решения для задачи 1:

php
// src/Event/CommentCreatedEvent.php
namespace App\Event;

use Symfony\Contracts\EventDispatcher\Event;
use App\Entity\Comment;

class CommentCreatedEvent extends Event
{
    public const NAME = 'comment.created';
    
    public function __construct(private Comment $comment) {}
    
    public function getComment(): Comment
    {
        return $this->comment;
    }
}

Сегодня мы разобрали:

  1. Как работает EventDispatcher в Symfony.
  2. Создание кастомных событий и их диспетчеризацию.
  3. Разницу между слушателями и подписчиками.
  4. Реальный пример отправки email после регистрации.

Эти знания помогут создавать расширяемые и легко поддерживаемые приложения. Хочешь освоить Symfony? Полный курс по Symfony для начинающих

Поделиться статьей:
Поддержать автора блога

Поддержка автора осуществляется с помощью специальной формы ниже, предоставленной сервисом «ЮMoney». Все платёжные операции выполняются на защищённой странице сервиса, что обеспечивает их корректность и полную безопасность.

Персональные рекомендации
Оставить комментарий