Как мне реализовать hook_menu ()?

103

Каковы основные принципы реализации hook_menu()?

Я хотел бы видеть основы, освещенные в одном вопросе, чтобы избежать необходимости отвечать на одни и те же похожие, но разные вопросы снова и снова.

Letharion
источник

Ответы:

148

Эта информация действительна для Drupal 6 и 7. В Drupal 8 hook_menu()была заменена новая система маршрутизации . Ниже мы реализуем hook_menu()в три простых шага.

Шаг первый

Создайте пустой модуль, следуя инструкциям в разделе Как создать пустой модуль . В показанном здесь коде предполагается, что модуль называется helloworld .

Шаг второй

Добавьте следующий код в файл модуля.

/**
 * Implements hook_menu().
 */
function helloworld_menu() {
  $items['hello'] = array(
    'title' => 'Hello world!',
    'page callback' => 'helloworld_page',
    'access callback' => TRUE,
  );

  return $items;
}

/**
 * Page callback for /hello.
 */
function helloworld_page() {
  return 'Hello world!';
}

Шаг третий

Включите модуль и посетите http://example.com/hello . (Замените example.com доменным именем для вашего сервера.)
Вы должны увидеть сообщение «Hello world!». Это оно! У вас полностью рабочая hook_menu()реализация. Далее следуют различные более сложные темы hook_menu(). В частности, вы можете захотеть прочитать о разрешениях, так как страница выше будет доступна любому.

аргументы

Если вы хотите передать больше данных обратному вызову страницы, вы можете использовать аргументы страницы для достижения этой цели. Аргументы страницы должны быть массивом аргументов для передачи обратному вызову страницы. Если в качестве аргумента используется целое число, оно будет представлять собой часть URL-адреса, начиная с 0, с однократным увеличением для каждой косой черты (/). В следующем примере это означает, что 0 будет превращен в «привет».

function helloworld_menu() {
  $items['hello'] = array(
    'page callback' => 'helloworld_page',
    'page arguments' => array(0),
  );

  return $items;
}

function helloworld_page($argument1) {
  return $argument1;
}

Строки будут отправлены дословно, поэтому array(0, 'world')могут быть использованы для hello worldвыхода снова.

function helloworld_page($argument1, $argument2) {
  return $argument1 . ' ' . $argument2;
}

«Подстановочные знаки» могут использоваться для приема произвольных данных из URL.

function helloworld_menu() {
  $items['hello/%'] = array(
    'page callback' => 'helloworld_page',
    'page arguments' => array(1),
  );

  return $items;
}

function helloworld_page($argument1) {
  return $argument1;
}

Посещение привет / мир, $argument1будет равным world.

Автозагрузка аргумента

Часто аргументом URL будет число, идентифицирующее, например, сущность. Чтобы избежать дублирования кода, который преобразует этот идентификатор в соответствующий объект, Drupal поддерживает автозагрузку для «именованных» подстановочных знаков. Когда используется именованный шаблон, Drupal проверит функцию с тем же именем, что и шаблон, с суффиксом _load. Если такая функция найдена, она будет вызываться со значением значения в URL, и все, что возвращается функцией-загрузчиком, будет передано обратному вызову страницы вместо исходного значения. Так как в Drupal уже есть такая функция для загрузки узлов, node_load()мы можем автоматически загружать узлы и передавать их на обратный вызов страницы.

function helloworld_menu() {
  $items['hello/%node'] = array(
    'page callback' => 'helloworld_page',
    'page arguments' => array(1),
  );

  return $items;
}

function helloworld_page($node) {
  return t('Hello node (ID = !nid)', array('!nid' => $node->nid));
}

Расширенная автозагрузка

Иногда необходимо автоматически загружать больше, основываясь на более чем одном аргументе. Поскольку по умолчанию в загрузчик передается только именованный аргумент, нужно явно указать Drupal, какие дополнительные аргументы загрузки следует передать загрузчику. Например, чтобы загрузить конкретную ревизию узла, необходимо перейти к node_load()идентификатору узла и идентификатору ревизии. Это может быть достигнуто с помощью следующего кода.

function helloworld_menu() {
  $items['hello/%node/revision/%'] = array(
    'page callback' => 'helloworld_page',
    'page arguments' => array(1),
    'load arguments' => array(3),
  );

  return $items;
}

function helloworld_page($node) {
  return t('Hello node (ID = !nid, revision ID = !rid)', array('!nid' => $node->nid, '!rid' => $node->vid));
}

права доступа

'access callback' => TRUE,необходимо сделать простой пример выше видимым для всех, но вряд ли он идеален, так как не позволяет контролировать то, что когда-либо. Любой, кто попытается посетить / привет, получит доступ. Самый простой способ обеспечить некоторую меру контроля - это обеспечить обратный вызов доступа, очень похожий на обратный вызов страницы сверху. Следующий код по-прежнему разрешает доступ кому угодно, но показывает, как переместить логику в функцию, вызываемую во время доступа, что позволяет использовать более сложную логику.

/**
 * Implements hook_menu().
 */
function helloworld_menu() {
  $items['hello'] = array(
    'page callback' => 'helloworld_page',
    'access callback' => 'helloworld_access',
  );

  return $items;
}

/**
 * Access callback for /hello.
 */
function helloworld_access() {
  return TRUE;
}

Это не обязательно лучший способ, поскольку использование пользовательской функции часто приводит к ненужному дублированию кода. Большую часть времени лучше использовать user_access(). Вместе с обратным вызовом доступа можно установить аргументы доступа. Можно потребовать, чтобы страница была доступна для просмотра пользователям с разрешением на доступ к профилям пользователя с помощью следующего кода.

/**
 * Implements hook_menu().
 */
function helloworld_menu() {
  $items['hello'] = array(
    'page callback' => 'helloworld_page',
    'access callback' => 'user_access',
    'access arguments' => array('access user profiles'),
  );

  return $items;
}

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

Более продвинутые темы

Официальная hook_menu()документация предоставляет гораздо больше информации о наиболее сложных сценариях использования хука.

Letharion
источник
3
Потрясающие! Просто для полноты, titleсвойство требуется для всех предметов, возвращенных изhook_menu()
Клайв
1
Я знаю, что в документах так сказано, но когда я тестировал в D7, это было не так. Я мог бы добавить его в первом примере я предполагаю, но я хотел бы сохранить вещи абсолютного минимума , чтобы сделать его как можно проще.
Летарион
1
Я бы сказал, что добавление заголовка на страницу - это одно из абсолютных минимальных действий, которые вы можете выполнять с hook_menu (), но это ваш пост: P Возможно, стоит исправить синтаксические ошибки (примеры кода 2, 4, 5 & 6), чтобы люди могли копировать и вставлять
Клайв
1
Ага, я изначально усердно выполнял код, но со временем я начал работать неаккуратно и начал копировать. Исправлены синтаксические ошибки, по крайней мере те, которые я видел;) Ну, вы правы, что должен быть заголовок, конечно, поэтому я добавил его в первоначальный пример.
Летарион
1
Как я могу зарегистрировать php-файл по пути, используя hook_menu? Это пользовательский php-файл, который включает в себя загрузчик drupal, использует переменные сеанса drupal и принимает аргумент, который является идентификатором пользователя.
Елин Й.