ertttert
Итак, мы знаем, что сигнатура функций для действий имеет вид example_action($object, $context)
. Рассмотрим каждый из этих параметров.
trigger.module
нужный объект передается действию в параметре $object
. Например, если действие предназначено для выполнения при создании новой ноды, параметр $object
будет содержать объект ноды.hooks
в функции hook_action_info()
. Но действиям, поддерживающим несколько триггеров, нужен способ определения контекста, в котором они вызваны, чтобы по-разному выполняться в зависимости от этого контекста.Предположим, что имеется веб-сайт, на котором возможны взаимоисключающие варианты. Вот один из коммерческих примеров: пользователи проходят платную регистрацию и могут оставлять на веб-сайте только один комментарий. После помещения одного комментария они блокируются и должны снова заплатить для разблокировки. Не задумываясь над экономическими перспективами такого сайта, рассмотрим, как можно реализовать необходимые триггеры и действия. Нам понадобится действие, блокирующее текущего пользователя. Просмотрев модуль user.module
, можно увидеть, в Drupal такое действие уже есть:
/** * Реализация hook_action_info(). */ function user_action_info() {return array( 'user_block_user_action' =>array( 'label' =>t( 'Block current user'),// Блокировка текущего пользователя 'type' => 'user', 'configurable' => FALSE, 'triggers' =>array() , ), ); }
Однако это действие не видно на странице назначения триггеров, т.к. оно не объявляет ни одного поддерживаемого хука: triggers
представляет собой просто пустой массив. Хорошо бы найти способ изменить это. И такой способ есть.
При выполнении хука action_info
Drupal позволяет каждому модулю не только объявить предоставляемые им действия, но и изменить эту информацию — в том числе и информацию, предоставляемую другими модулями. Вот как можно сделать действие Block current user
(Блокировка текущего пользователя) доступным для триггера вставки комментария:
/** * Реализация hook_drupal_alter(). Вызывается Drupal после вызова * hook_action_info(), чтобы модули могли изменить массив action_info. * * @param array $info * Результат вызова hook_action_info() для всех модулей. */ function beep_action_info_alter( &$info) {// Сделать действие Block current user доступным // для триггера вставки комментария. if (!
in_array( "comment_insert", $info['user_block_user_action']['triggers']) ) { $info['user_block_user_action']['triggers'][] = 'comment_insert'; } }
После этого действие Block current user
станет доступным для назначения, как показано на рис. 3.6.
Когда назначено действие, текущий пользователь будет блокироваться после размещения нового комментария. Рассмотрим, что при этом происходит. Мы уже знаем, что Drupal уведомляет модули о возникновении определенных событий за счет запуска хука. В нашем случае это хук комментария. Конкретная выполняемая операция — insert
(вставка), т.к. нужно реагировать на добавление нового комментария. Модуль триггера реализует хук комментария. Внутри такого хука выполняется запрос к базе данных, существуют ли действия, назначенные данному триггеру. База данных выдает информацию о назначенном нами действии Block current user
(Блокировка текущего пользователя). Теперь модуль триггера готов к выполнению действия, которое имеет стандартную сигнатуру функции example_action($object, $context)
. Но тут возникает одна проблема. Действие, которое должно быть выполнено, имеет тип user
(пользователь), а не comment
(комментарий). Оно ожидает, что ему будет передан объект пользователя! В контексте хука комментария нужно вызвать действие пользователя, но хуку передается информация о комментарии, а не о пользователе. И как быть? Что произойдет, если модуль триггера определит, что действие нужно выполнить для пользователя, и загрузит объект $user
, необходимый нашему действию? Вот код из модуля modules/trigger/trigger.module
, который демонстрирует, как это происходит:
/** * Загрузка связанных объектов для триггеров комментария. * * При вызове действия в контексте, который не соответствует его типу, должен * быть извлечен объект, ожидаемый действием. Например, если хук комментария * вызывает действие, обрабатывающее ноды, объект ноды недоступен, т.к. он не * передается хуку комментария. Поэтому здесь загружается необходимый * действию объект. * * @param $type * Тип действия, которое должно быть выполнено. * @param $comment * Комментарий, передаваемый через хук комментария. * * @return * Объект, нужный для действия, которое должно быть выполнено. */ function _trigger_normalize_comment_context( $type, $comment) {switch ( $type) {// Действие для работы с нодами вызвано в контексте комментария. case 'node':return node_load(is_array( $comment) ? $comment['nid'] : $comment->nid);// Действие для работы с пользователями вызвано в контексте комментария. case 'user':return user_load(is_array( $comment) ? $comment['uid'] : $comment->uid); } }
При выполнении этого кода для нашего действия пользователя срабатывает второй вариант: загружается объект пользователя и затем выполняется действие пользователя. Информация, известная хуку комментария (например, тема комментария), передается действию в параметре $context
. Обратите внимание, что действие ищет идентификатор пользователя сначала в объекте, потом в контексте, и затем переходит к глобальной информации в $user
:
/** * Блокировка текущего пользователя. * * @ingroup actions */ function user_block_user_action( &$entity, $context =array() ) {if (isset( $entity->uid) ) { $uid = $entity->uid; }elseif (isset( $context['uid']) ) { $uid = $context['uid']; }else { global $user; $uid = $user->uid; }db_update( 'users') ->fields(array( 'status' => 0) ) ->condition( 'uid', $uid) ->execute() ;drupal_session_destroy_uid( $uid) ;watchdog( 'action', 'Blocked user %name.',array( '%name' => $user->name)) ; }
Действия должны вести себя интеллектуально, поскольку они не очень-то знают, что происходит при их вызове. Поэтому лучшие действия выглядят просто и даже являются атомарными. Модуль триггера всегда передает текущий хук и действие в контекст. Эти значения хранятся в элементах $context['hook']
и $context['op']
. Такой подход предлагает стандартный способ предоставления информации действию.
Всего комментариев: 0 | |