Extra Block Types (EBT) - New Layout Builder experience❗

Extra Block Types (EBT) - styled, customizable block types: Slideshows, Tabs, Cards, Accordions and many others. Built-in settings for background, DOM Box, javascript plugins. Experience the future of layout building today.

Demo EBT modules Download EBT modules

❗Extra Paragraph Types (EPT) - New Paragraphs experience

Extra Paragraph Types (EPT) - analogical paragraph based set of modules.

Demo EPT modules Download EPT modules

Scroll

12.12. Event Dispatcher, кастомный код для определенных событий.

19/11/2019, by Ivan

Система events позволяет строить более сложные системы с возможностью изменения функционала с помощью кастомного кода по определенным событиям. Многие хуки из Drupal 7 были заменены event'ами. Это позволило унифицировать работу многих частей друпала и дополнительных контрибных модулей. Сама система events пришла из Symfony и состоит из следующих частей:

Event Subscribers - "Подписчики" на определенные события, это функции или методы, которые срабатывают по определенным событиям. В коде является классом, который реализует класс:

\Symfony\Component\EventDispatcher\EventSubscriberInterface

Event Registry - Собирает и сортирует по очередности срабатывания Event Subscribers. Registry для subscribers хранимых в объекте Event Dispatcher'а как массив ключ-значение имени события и приоритет события (порядок). Когда событие регистрируется как service, событие регистрируется как глобально доступный dispatcher.

Event Dispatcher - Механизм в котором событие срабатывает и позволяет вызывать в нужный момент Event Subscribers. Главным образом по меньшей мере один из экземляров Event Dispatcher'a представлен как service. Класс Event Dispatcher'a реализует: \Symfony\Component\EventDispatcher\EventDispatcherInterface.

Event Context - Многие события требуют специфического набора данных, что может быть вазжно для subscriber'а на event. Это может быть простое значение переданное в Event Subscriber, а может быть и класс который содержит нужные данные. Класс Event Context расширяет класс: \Symfony\Component\EventDispatcher\Event.

Давайте разберем примеры работы с событиями, чтобы стало понятно как это работает.

Я добавил весь код на github в модуль drupalbook_examples, вы можете скачать модуль и добавить его к себе на сайт:

https://github.com/levmyshkin/drupalbook8

В Drupal 8 больше нет hook_init():

https://www.drupal.org/node/2013014

Теперь выполнить нужный код при загрузке страницы можно с помощью Event Subscriber'a:

modules/custom/drupalbook_examples/drupalbook_examples.services.yml

services:
  drupalbook_examples.event_subscriber:
    class: Drupal\drupalbook_examples\EventSubscriber\DrupalbookExamplesSubscriber
    tags:
    - {name: event_subscriber}

Для того чтобы подключить Event Subscriber нужно добавить сервис в файл модуля *.services.yml. Здесь мы описываем какой класс будет Event subscriber'ом и пишем в тегах имя event_subscriber. Теперь нужно создать класс event subscriber:

modules/custom/drupalbook_examples/src/EventSubscriber/DrupalbookExamplesSubscriber.php

namespace Drupal\drupalbook_examples\EventSubscriber;
 
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
 
class DrupalbookExamplesSubscriber implements EventSubscriberInterface {
 
  public function checkForRedirection(GetResponseEvent $event) {
    if ($event->getRequest()->query->get('redirect-me')) {
      $event->setResponse(new RedirectResponse(\Drupal::url('<front>', [], ['absolute' => TRUE])));
    }
  }
 
  /**
   * {@inheritdoc}
   */
  public static function getSubscribedEvents() {
    $events[KernelEvents::REQUEST][] = array('checkForRedirection');
    return $events;
  }
 
}

Давайте разберем основные моменты этого кода. Класс нашего event subscriber реализует интерфейс EventSubscriberInterface, основным методом этого интерфейса является getSubscribedEvents(), он определяет на какие события будет реагировать наш subscriber и какие методы он будет вызывать в случае если событие произойдет. В нашем случает событие произойдет при самом начале работы друпала после обработки запроса. Здесь мы вызывает метод checkForRedirection() если происходит событие. В самом методе если у нас есть get параметр redirect-me, то перенаправляет пользователя на сайт example.com.

Этот код будет работать, всегда когда вы будете загружать страницу, ну или почти всегда. Дело в том, если ваша страница закеширована, то она может быть отдана из кеша и друпал не будет отрабатывать все куски кода и сразу сработает кеш. В этом случае можно использовать аналог хука hook_boot(), который также как и hook_init() удален из Drupal 8:

https://www.drupal.org/node/1909596

Давайте добавим еще метод redirectBeforeWithoutCache()

modules/custom/drupalbook_examples/src/EventSubscriber/DrupalbookExamplesSubscriber.php:

namespace Drupal\drupalbook_examples\EventSubscriber;
 
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
 
class DrupalbookExamplesSubscriber implements EventSubscriberInterface {
 
  public function checkForRedirection(GetResponseEvent $event) {
    if ($event->getRequest()->query->get('redirect-me')) {
      $event->setResponse(new RedirectResponse(\Drupal::url('<front>', [], ['absolute' => TRUE])));
    }
  }
 
  public function redirectBeforeWithoutCache(GetResponseEvent $event) {
    if ($event->getRequest()->query->get('redirect-me')) {
      $event->setResponse(new RedirectResponse(\Drupal::url('<front>', [], ['absolute' => TRUE])));
    }
  }
 
  /**
   * {@inheritdoc}
   */
  public static function getSubscribedEvents() {
    $events[KernelEvents::REQUEST][] = array('checkForRedirection');
    $events[KernelEvents::REQUEST][] = array('redirectBeforeWithoutCache', 300);
    return $events;
  }
 
}

В методе getSubscribedEvents() мы зарегистрировали ответ на тоже событие KernelEvents::REQUEST, только в этом случае мы установили приоритет 300, что позволяет сработать этому методу раньше, чем методы которые сработают для этого события, но у которых меньший приоритет. Теперь редирект сработает даже для закешированной страницы.

В друпале довольно много событий, которые можно добавить в Subscriber, больше примеров вы сможете найти в официальной документации:

https://www.drupal.org/docs/8/creating-custom-modules/event-systems-overview-how-to-subscribe-to-and-dispatch-events