Encodera

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.

Оглавление

События

Событие — сообщение «произошло вот что». Код, который его отправил, не знает, кто отреагирует. Так к действию можно добавить новые последствия — письмо, запись в журнал, пересчёт статистики, — не трогая исходный код. На событиях построен и сам Symfony.

Слушатель события ядра

Пока Symfony обрабатывает запрос, он отправляет несколько событий. Слушатель — класс с атрибутом #[AsEventListener]:

// src/EventListener/ResponseHeaderListener.php
namespace App\EventListener;
 
use Symfony\Component\EventDispatcher\Attribute\AsEventListener;
use Symfony\Component\HttpKernel\Event\ResponseEvent;
 
#[AsEventListener]
class ResponseHeaderListener
{
    public function __invoke(ResponseEvent $event): void
    {
        $event->getResponse()->headers->set('X-Frame-Options', 'DENY');
    }
}

Какое событие слушать, Symfony определяет по типу аргумента. Теперь заголовок добавляется к каждому ответу.

События ядра

СобытиеКогда происходит
RequestEventЗапрос получен, контроллер ещё не выбран
ControllerEventКонтроллер выбран, но не вызван
ResponseEventОтвет готов к отправке
ExceptionEventВозникло исключение
TerminateEventОтвет уже отправлен

Своё событие

Событие — обычный класс с данными:

// src/Event/PostPublished.php
namespace App\Event;
 
use App\Entity\Post;
 
class PostPublished
{
    public function __construct(public readonly Post $post) {}
}

Отправляет его диспетчер:

use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
 
public function publish(Post $post, EntityManagerInterface $em, EventDispatcherInterface $dispatcher): Response
{
    $post->setPublished(true);
    $em->flush();
 
    $dispatcher->dispatch(new PostPublished($post));
 
    return $this->redirectToRoute('blog_show', ['id' => $post->getId()]);
}

Слушатели своего события

#[AsEventListener]
class NotifySubscribers
{
    public function __construct(private MailerInterface $mailer) {}
 
    public function __invoke(PostPublished $event): void
    {
        // отправить письма подписчикам о $event->post
    }
}

Слушателей может быть сколько угодно. Контроллер о них не знает.

Подписчик

Подписчик — класс, который слушает несколько событий и сам перечисляет их:

use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\KernelEvents;
 
class LocaleSubscriber implements EventSubscriberInterface
{
    public static function getSubscribedEvents(): array
    {
        return [
            KernelEvents::REQUEST => ['onRequest', 20],
        ];
    }
 
    public function onRequest(RequestEvent $event): void
    {
        $request = $event->getRequest();
        $request->setLocale($request->getSession()->get('locale', 'ru'));
    }
}

Число 20 — приоритет: чем оно больше, тем раньше вызывается метод.

События Doctrine

Сущность может реагировать на собственное сохранение:

#[ORM\Entity]
#[ORM\HasLifecycleCallbacks]
class Post
{
    #[ORM\PrePersist]
    public function setCreatedAtValue(): void
    {
        $this->createdAt = new \DateTimeImmutable();
    }
}

Список слушателей

php bin/console debug:event-dispatcher
php bin/console debug:event-dispatcher kernel.request

Частая ошибка

Слушатели событий ядра выполняются при каждом запросе. Медленная работа в них — запрос к внешнему сервису, тяжёлый расчёт — замедлит весь сайт. Долгие задачи из слушателя отправляют в очередь, а сам слушатель оставляют коротким.

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.