Найти тему
Topsite Web

Хуки и события в Drupal

Оглавление

Для разработчиков Drupal очень важно понимать две фундаментальные концепции: события и хуки. Потому что это самые мощные способы обеспечения настройки и расширяемости в Drupal. Событие - это системное или модульное вхождение, которое вызывает определенные действия, в то время как хук - это функция обратного вызова, которая позволяет разработчикам взаимодействовать, изменять или расширять поведение ядра Drupal или любого модуля.

Что такое события в Drupal

События похожи на крючки, которые говорят Drupal вызвать вашу функцию, если что-то случится. Мы можем сказать, что события являются объектно-ориентированной системой крючков. События Drupal позволяют различным компонентам системы взаимодействовать и общаться друг с другом независимо или отдельно.

Характеристики

  • События являются частью более широкого внедрения Drupal фреймворка Symfony.
  • События отправляются определенными действиями или триггерами в системе. Вы можете отправлять события при написании пользовательского кода, чтобы уведомлять другие компоненты системы о действиях, предпринятых вашим кодом.
  • Разработчики могут подписаться на эти события и определить пользовательские действия, которые будут выполняться.

Пример

  • Событие ядра Drupal: KernelEvents::REQUEST
  • Сценарий: Реализация пользовательского модуля, который прослушивает событие REQUEST для выполнения определенных действий до обработки запроса.
// MyModuleEventSubscriber.php
namespace Drupal\my_module\EventSubscriber;

use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\HttpKernel\Event\RequestEvent;

class MyModuleEventSubscriber implements EventSubscriberInterface {

  public static function getSubscribedEvents() {
    $events[KernelEvents::REQUEST][] = ['onRequestEvent'];
    return $events;
  }
  public function onRequestEvent(RequestEvent $event) {
    // Пользовательская логика, которая будет выполняться при каждом запросе.
  }
}

Просмотр существующих событий

Существуют различные методы поиска существующих событий:

Использование модуля WebProfiler:

  1. Загрузите и включите WebProfiler и модуль Devel, так как WebProfiler зависит от модуля Devel
  2. Затем перейдите в Управление > Конфигурация > Настройки разработки > WebProfiler, а затем установите флажок, чтобы активировать элемент панели инструментов "События".
  3. Теперь, когда вы посещаете любую страницу на своем сайте, вы должны увидеть панель инструментов WebProfiler в нижней части страницы, и после нажатия на значок панели инструментов событий вы получите список всех подписчиков событий и информацию, которая вызывается во время этого запроса.

Используйте Devel для просмотра и класса события:

drush devel:event

 Enter the number for which you want to get information.:
  [0] kernel.controller
  [1] kernel.exception
  [2] kernel.request
  [3] kernel.response
  [4] kernel.terminate
  [5] kernel.view
 > 0

 Enter the number to view the implementation.:
  [0] Drupal\path_alias\EventSubscriber\PathAliasSubscriber::onKernelController
  [1] Drupal\Core\EventSubscriber\EarlyRenderingControllerWrapperSubscriber::onController
  [2] Drupal\webprofiler\DataCollector\RequestDataCollector::onKernelController
 > 0

Поиск в вашей кодовой базе для @Event:

В вашем редакторе, таком как Visual Studio или PHPStorm, найдите текст @Event в параметре маски файла: *.php.



Подписаться на событие

Как мы знаем, Drupal использует архитектуру, управляемую событиями, где различные компоненты могут взаимодействовать друг с другом, отправляя и подписываясь на Events.

Вот пример подписки на событие в Drupal.

1. Определение и событие подписчика

# MyModule/my_module.services.yml
services:
  my_module.event_subscriber:
    class: Drupal\my_module\EventSubscriber\MyModuleEventSubscriber
    tags:
      - { name: event_subscriber }

2. Определите класс подписчика события

// MyModule/src/EventSubscriber/MyModuleEventSubscriber.php

namespace Drupal\my_module\EventSubscriber;

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

/**
* Class MyModuleEventSubscriber.
*/
class MyModuleEventSubscriber implements EventSubscriberInterface {

  /**
  * {@inheritdoc}
  */
  public static function getSubscribedEvents() {
    // Укажите события, на которые нужно подписаться, и метод, который будет вызываться при возникновении события.
    $events = [
      'node.insert' => 'onNodeInsert',
      'user.login' => 'onUserLogin',
    ];
    return $events;
  }

  /**
  * Реагируйте на событие вставки узла.
  */
  public function onNodeInsert(Event $event) {
    // Ваша логика.
    \Drupal::logger('my_module')->notice('Node inserted!');
  }
  /**
  * Реагируйте на событие входа пользователя в систему.
  */
  public function onUserLogin(Event $event) {
    // Ваша логика.
    \Drupal::logger('my_module')->notice('User logged in!');
  }

}

В этом примере:

  • MyModuleEventSubscriber - это класс, который реализует EventSubscriberInterface.
  • Метод getSubscribedEvents определяет, какие события интересуют абонента и какой метод вызывать при каждом событии.
  • Методы onNodeInsert и onUserLogin содержат логику, которую вы хотите выполнить при возникновении соответствующих событий.

Отправка события

Чтобы позволить другому разработчику подписаться на события и реагировать соответствующим образом, вы можете отправить событие в своих модулях или подмодулях. Прежде чем отправить событие, нам нужно понять, когда отправлять событие. Вы можете отправить событие, если хотите расширить свою логику без обновления существующего кода. События могут быть отправлены в любое время, например, создание, обновление, загрузка или удаление данных, управляемых вашим модулем.

Давайте объясним это на примере.

Возьмите сценарий, в котором мы хотим, чтобы другие разработчики взаимодействовали, когда после отправки пользовательской формы создается новая сущность.

1. Создайте пользовательский модуль:

# Создайте каталог модуля
mkdir modules/custom/custom_logger

# Создайте файл модуля
touch modules/custom/custom_logger/custom_logger.info.yml

2. В custom_logger.info.yml добавьте следующий контент:

name: 'Custom Logger'
type: module
description: 'Пользовательский модуль для ведения журнала событий.'
core_version_requirement: ^8 || ^9 || ^10
package: Custom

3. Создать событие:

// modules/custom/custom_logger/src/Event/CustomLoggerEvent.php
namespace Drupal\custom_logger\Event;

use Symfony\Component\EventDispatcher\Event;

/**
* Определяет пользовательское событие для модуля custom_logger.
*/
class CustomLoggerEvent extends Event {

  /**
  * Узел, который запустил это событие.
  *
  * @var \Drupal\node\Entity\Node
  */
  protected $node;
  /**
  * CustomLoggerEvent constructor.
  *
  * @param \Drupal\node\Entity\Node $node
  *   Узел, который запустил это событие.
  */
  public function __construct($node) {
    $this->node = $node;
  }
  /**
  * Получите объект node.
  *
  * @return \Drupal\node\Entity\Node
  *   Узел.
  */
  public function getNode() {
    return $this->node;
  }

}

4. Отправка события: создание любой сущности и отправка пользовательского события с созданной сущностью в качестве параметра.

// modules/custom/custom_logger/src/Form/MyCustomForm.php

namespace Drupal\custom_logger\Form;

use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\custom_logger\Event\CustomLoggerEvent;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;

/**
* Моя пользовательская форма.
*/
class MyCustomForm extends FormBase {

  /**
  * Служба диспетчеризации событий.
  *
  * @var \Symfony\Component\EventDispatcher\EventDispatcherInterface
  */
  protected $eventDispatcher;

  /**
  * Создает новый объект MyCustomForm.
  *
  * @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $event_dispatcher
  *   Служба диспетчеризации событий.
  */
  public function __construct(EventDispatcherInterface $event_dispatcher) {
    $this->eventDispatcher = $event_dispatcher;
  }

  /**
  * {@inheritdoc}
  */
  public static function create(ContainerInterface $container) {
    return new static(
      $container->get('event_dispatcher')
    );
  }

  /**
  * {@inheritdoc}
  */
  public function getFormId() {
    return 'my_custom_form';
  }

  /**
  * {@inheritdoc}
  */
  public function buildForm(array $form, FormStateInterface $form_state) {
    // Создайте свои элементы формы здесь.
    return $form;
  }

  /**
  * {@inheritdoc}
  */
  public function submitForm(array &$form, FormStateInterface $form_state) {
    // Обработайте отправку формы и создайте новый узел.
    // Отправьте пользовательское событие.
    $node = $this->getCreatedNode(); // Реализуйте этот метод на основе вашего варианта использования.
    $event = new CustomLoggerEvent($node);
    $this->eventDispatcher->dispatch('custom_logger.event', $event);

    // Выполните любые дополнительные действия после отправки события.
  }
}

5. Реагирование на событие — теперь другие модули или части приложения могут подписаться на custom_logger.event с созданным узлом в качестве параметра.

Что такое хуки в Drupal

Хуки позволяют модулям изменять и расширять существующее поведение Drupal Core или любого другого модуля без изменения существующего кода.

Характеристики

  • Традиционный способ Drupal, позволяющий модулям взаимодействовать с системой.
  • Код может быть улучшен или изменен независимо и постепенно.
  • Хуки - это предопределенные функции с определенными именами, которые ядро или модули Drupal вызывают в различных точках во время выполнения.
  • Разработчики реализуют эти функции в своих модулях, чтобы расширить или изменить поведение по умолчанию.
  • Очень эффективный и простой в реализации.

Типы хуков

  1. Хуки, которые реагируют на события: например, когда пользователь входит в систему и необходимо выполнить некоторые действия. Это очень похоже на события. Это вызывается при выполнении конкретных действий. Например, hook_user_cancel().
  2. Крючки, которые отвечают на вопросы. Они вызываются, когда какой-то компонент собирает информацию по определенной теме. Эти крючки возвращают массивы, структура и значения которых определяются в определении крючка. Например, смотрите пользовательский модуль hook user_toolbar(), который добавляет ссылки на общую страницу учетной записи пользователя на панель инструментов. Примечание: В Drupal 8 или более поздних версиях это обычно обрабатывается системой плагинов. Поэтому сейчас очень мало хуков, чем в Drupal 7.
  3. Крючки, которые изменяют существующие данные обычно имеют суффикс с alter. Они вызываются, чтобы позволить модулям изменять существующий код. Например: hook_form_alter().

Пример:

  • Основной хук Drupal: hook_form_alter()
  • Сценарий: Изменение формы, определенной другим модулем.
// my_module.module
function my_module_form_alter(&$form, \Drupal\Core\Form\FormStateInterface $form_state, $form_id) {
  // Пользовательская логика для изменения формы.
}

Просмотр существующих хуков

Хуки могут быть определены в пользовательских модулях, даже несмотря на то, что есть некоторые хуки, вызываемые основными подсистемами Drupal, такими как Form API, которые всегда присутствуют. Иногда может быть немного сложно узнать, какие крючки доступны и какие из них реализовать.

Существуют различные способы обнаружения существующих крючков:

  1. Ищите определения крючков в файлах *.api.php, содержащихся либо в ядре Drupal, либо в любых внесенных модулях.
  2. Вы можете использовать свою IDE для поиска функций, имя которых начинается с hook_.
  3. Вы можете получить полный список крючков здесь.
  4. Другой способ - использовать команду drush, которая даст вам список всех реализаций конкретного хука.
drush fn-hook help #Alias of drush devel:hook

Вызывать новый хук

  1. Чтобы позволить другим разработчикам изменять или расширять нашу функцию, вы должны вызвать крюк или, в качестве альтернативы, отправить событие.
  2. Это можно сделать при любом действии, таком как создание, удаление, обновление или событие, когда мы получаем или передаем некоторые данные через API.
  3. Хуки вызываются с помощью служб module_handler \Drupal::moduleHandler().
  4. Хуки могут быть вызваны различными способами:
  • Выполните крюк в каждом модуле, который его реализует: ModuleHandler::invokeAll()
  • Выполните хук для каждого модуля, обычно переключая список включенных модулей:ModuleHandler::invoke()
  • Вызов изменения, позволяющего изменять существующие структуры данных с помощью ModuleHandler::alter().

Определите новый хук

Чтобы определить новый крючок, вы должны сделать следующее:

1. Выберите уникальное имя для своего крючка

2. Задокументируйте свой крючок

Хуки задокументированы в файле {MODULE_NAME}.api.php:

// custom_hooks.api.php
/**
* Определите пользовательский хук для реагирования на определенные события.
*
* Этот хук вызывается, когда в системе происходит определенное событие.
* Модули могут реализовать этот хук для выполнения дополнительных действий в ответ на событие.
*
* @param string $param1
*   Пример параметра для перехвата.
* @param array $param2
*   Еще один пример параметра для перехвата.
*
* @ingroup custom_hooks_hooks
*/
function hook_custom_event($param1, array $param2) {
  // Здесь ваша пользовательская логика подключения.
}

3. Вызов вашего крючка в коде модуля:

/**
* Implements hook_ENTITY_TYPE_view().
*/
function hooks_example_node_view(array &$build, EntityInterface $entity, EntityViewDisplayInterface $display, $view_mode) {
  // Вызовите перехватчик, чтобы предупредить другие модули о том, что счетчик был обновлен.
  $module_handler = \Drupal::moduleHandler();
  // В этом примере мы вызываем функцию hook_custom_event()
  $module_handler->invokeAll('custom_event', [$entity]);

}

Когда использовать события или хуки

  • События — когда действия должны быть разделены или при интеграции с компонентами Symfony.
  • Хуки — используйте для более простых модификаций или при взаимодействии с ядром Drupal.

Плюсы и минусы использования

События:

  • Плюсы: Развязка, лучшая организация и интеграция с Symfony.
  • Минусы: Чуть более крутая кривая обучения для тех, кто не знаком с Symfony.

Хуки:

  • Плюсы: Простота, хорошо зарекомендовавшая себя в Drupal, проще для Drupal - конкретные задачи.
  • Минусы: более плотная связь, меньшая организация в крупных проектах.

Заключение

Понимание событий и крючков чрезвычайно важно для эффективной разработки Drupal. В то время как хуки являются традиционными механизмами Drupal для расширения функциональности, события были введены с Drupal как часть его архитектуры, управляемой событиями. Выбор правильного механизма должен основываться на сложности и характере проекта Drupal.