События и подписчики
Событие — сообщение «сейчас произошло то-то», которое ядро рассылает всем желающим. Подписчик — класс, который слушает нужные события и выполняет свой код. Это второй, наряду с хуками, способ вмешаться в работу Drupal. Событиями оформлена та часть ядра, что пришла из Symfony: обработка запроса и ответа, маршруты, сохранение настроек.
Где лежит подписчик
Подписчики принято класть в папку src/EventSubscriber модуля. Создайте папку и файл вручную.
Класс подписчика
Добавим ко всем ответам сайта свой заголовок:
// web/modules/custom/blog/src/EventSubscriber/HeaderSubscriber.php
namespace Drupal\blog\EventSubscriber;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\Event\ResponseEvent;
use Symfony\Component\HttpKernel\KernelEvents;
class HeaderSubscriber implements EventSubscriberInterface
{
public static function getSubscribedEvents(): array
{
return [KernelEvents::RESPONSE => 'onResponse'];
}
public function onResponse(ResponseEvent $event): void
{
$event->getResponse()->headers->set('X-Blog', 'Drupal course');
}
}Метод getSubscribedEvents() сообщает, какие события слушать и какой метод вызывать. KernelEvents::RESPONSE происходит, когда ответ готов к отправке. В метод приходит объект события — из него получают ответ и меняют его.
Регистрация
Подписчик — это сервис с особой меткой. Добавьте его в blog.services.yml:
# web/modules/custom/blog/blog.services.yml
services:
blog.header_subscriber:
class: Drupal\blog\EventSubscriber\HeaderSubscriber
tags:
- { name: event_subscriber }Метка event_subscriber в разделе tags сообщает Drupal, что сервис нужно подключить к рассылке событий. Если в файле уже есть раздел services, новую запись добавляют в него, не повторяя слово services.
Проверка
vendor/bin/drush cr
curl -I http://127.0.0.1:8888/blogВ заголовках ответа появится строка X-Blog: Drupal course. Она же видна в инструментах разработчика браузера на вкладке «Сеть».
Частые события
| Событие | Когда происходит |
|---|---|
KernelEvents::REQUEST | Запрос получен, контроллер ещё не выбран |
KernelEvents::RESPONSE | Ответ готов к отправке |
KernelEvents::EXCEPTION | Во время обработки возникло исключение |
ConfigEvents::SAVE | Сохранён объект конфигурации |
RoutingEvents::ALTER | Собран список маршрутов: можно изменить чужой маршрут |
Классы ConfigEvents и RoutingEvents находятся в пространствах имён Drupal\Core\Config и Drupal\Core\Routing.
Приоритет
Если на событие подписано несколько классов, порядок задаёт приоритет — число рядом с именем метода:
// web/modules/custom/blog/src/EventSubscriber/HeaderSubscriber.php
public static function getSubscribedEvents(): array
{
return [KernelEvents::RESPONSE => ['onResponse', 100]];
}Чем больше число, тем раньше вызывается подписчик. Без числа приоритет равен нулю.
Хук или событие
Выбирать не приходится: способ определяет ядро. Сущности, формы и шаблоны расширяются хуками, а запрос, ответ, маршруты и конфигурация — событиями. В документации каждого места указано, что именно оно предлагает.
Частая ошибка
Класс подписчика написан, а метод не вызывается. Причина — подписчик не зарегистрирован: Drupal не ищет такие классы по папке, как плагины. Нужна запись в blog.services.yml с меткой event_subscriber и затем drush cr. Проверьте и отступы в YAML: tags стоит на том же уровне, что class.