Создание собственного хука и альтера

Создание собственного хука и альтера

Хуки (от англ. hook - крюк) предоставляют точки входа для изменения параметров (например объектов, массивов), получения информации, выполнения дополнительных операций. Использование хуков позволяет расширять функционал приложения (сайта), не вмешиваясь в ядро и контрибные модули. Часть хуков Drupal предоставляет из коробки, другую часть реализуют контрибные модули. Важно, при написании своего модуля, предоставлять возможность изменять данные другими пользователями. Этим вы значительно облегчите работу разработчикам, которые решат использовать ваш модуль. Разумеется, размещение хуков должно быть к месту и по делу.

Содержание

Создание хука

Рассмотрим создание собственного хука на простом примере выпадающего списка.

1
2
3
4
5
6
7
8
9
$form['select'] = array(
  '#type' => 'select',
  '#title' => t('Choose the option'),
  '#options' => array(
    'option 1',
    'option 2',
    'option 3',
  ),
);

выпадающий список
Позволим сторонним модулям дополнять массив опций селекта. Для этого перепишем вышеприведенный кусок кода с реализацией собственного хука MYMODULE_get_select_options.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// Предустановленные значения.
$options = array(
  'option 1',
  'option 2',
  'option 3',
);
 
// Позволим другим модулям дополнить данный список.
foreach (module_invoke_all('MYMODULE_get_select_options') as $option) {
  $options[] = $option;
}
 
$form['select'] = array(
  '#type' => 'select',
  '#title' => t('Choose the option'),
  '#options' => $options,
);

Функция module_invoke_all()[1] позволяет "пробежаться" по всем включенным модулям в поисках объявленного хука и вызвать его. Для вызова хука также может использоваться функция module_invoke(). Отличия от module_invoke_all() в том, что она позволяет вызывать хук определенного модуля.
Т.к. мы создали собственный хук, то хорошим тоном является создание файла MYMODULE.api.php с описанием всех собственных хуков. Данный файл является справочным и указывает на то, как использовать хуки, какие аргументы принимать и что возвращать.
В MYMODULE.api.php

1
2
3
4
5
6
7
8
9
10
11
12
13
/**
 * Defines options elements used in drop-down menu.
 *
 * @return array
 *   Array of string options.
 */
function hook_MYMODULE_get_select_options() {
  return array(
    'example option 1',
    'example option 2',
    'example option 3',
  );
}

Пример использования хука в стороннем модуле MODULE1.

1
2
3
4
5
6
7
8
9
10
/**
 * Implements hook_MYMODULE_get_select_options().
 */
function MODULE1_MYMODULE_get_select_options() {
  return array(
    'test option 1',
    'test option 2',
    'test option 3',
  );
}

дополненный список

Создание альтера

Функция drupal_alter()[2] позволяет изменять данные, передаваемые вторым аргументом по ссылке. В приведенном выше примере с селектом, добавим возможность изменения списка опций.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// Предустановленные значения.
$options = array(
  'option 1',
  'option 2',
  'option 3',
);
 
// Позволим другим модулям дополнить данный список.
foreach (module_invoke_all('MYMODULE_get_select_options') as $option) {
  $options[] = $option;
}
 
// Позволим другим модулям вносить изменения в массив опций.
drupal_alter('MYMDOULE_select_options', $options);
 
$form['select'] = array(
  '#type' => 'select',
  '#title' => t('Choose the option'),
  '#options' => $options,
);

Пример реализации хука hook_MYMDOULE_select_options_alter(&$options) в стороннем модуле MODULE2.

1
2
3
4
5
6
7
/**
 * Implements hook_MYMDOULE_select_options_alter().
 */
function MODULE2_MYMDOULE_select_options_alter(&$options) {
  // Удалим 1-ый элемента массива options.
  array_shift($options);
}

список после альтера

Изменение порядка вызова хуков

Как известно, по умолчанию последовательность хуков определяется весом модуля в таблице system. Т.е. повлиять на очередность вызова хуков можно следующим образом:

  1. изменить вес модуля (чем больше вес, тем более низкую позицию займет хук в общей очередности)
  2. использовать хук hook_module_implements_alter()[3]

Первый вариант обычно используется в hook_install() кастомного модуля. Изменить вес инсталлируемого модуля можно следующим образом:
в файле MYMODULE.install

1
2
3
4
5
6
7
8
9
10
11
12
/**
 * Implements hook_install().
 */
function MYMODULE_install() {
  db_update('system')
    ->fields(array(
      'weight' => 999,
    ))
    ->condition('name', 'MYMODULE')
    ->condition('type', 'module')
    ->execute();
}

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

Дополнительная информация по статье

  1. https://api.drupal.org/api/drupal/includes!module.inc/function/module_invoke_all/7 - описание функции module_invoke_all().
  2. https://api.drupal.org/api/drupal/includes!module.inc/function/drupal_alter/7 - описание функции drupal_alter().
  3. https://api.drupal.org/api/drupal/modules!system!system.api.php/function/hook_module_implements_alter/7 - описание хука hook_module_implements_alter().