УрокТонкости программного создания страниц на сайте
Первое, о чём я хочу сегодня рассказать - создание страниц с переменными аргументами. То есть, например, для страниц node/10, node/20, node/123 схема была задана лишь один раз, а используется она для всех страниц, которые подходят под шаблон node/%. Задаются подобные страницы следующим образом:
function mymodule_menu() { $items = array(); $items['foo/%/bar'] = array( 'page callback' => 'mymodule_foobar_page', 'page arguments' => array(1), ); return $items; }
При задании такой схемы страницы, функция mymodule_foobar_page() будет вызываться для страниц, урл которых начинается на foo, далее идёт любое значение (не обязательно численное), далее bar. Примеры: foo/1/bar, foo/my/bar, foo/10000000/bar. А вот такой пример под нашу схему уже не подходит: foo/1/2/bar.
Здесь появился ключ page arguments, указывающий номер аргументов из урла, которые будут переданы в функцию mymodule_foobar_page(). В нашем случае я указал единицу - значит, что будет передан "неизвестный" аргумент (который идёт с символом %). Не забывайте, что у программистов счёт начинается с нуля :) Если бы я указал 'page arguments' => array(0), то в качестве аргумента передалось бы слово 'foo'. А если бы я указал 'page arguments' => array(0, 1), то передались бы оба агрумента - слово 'foo', и переменное значение.
Пример. Если страница имеет урл foo/10/bar, то при 'page arguments' => array(1) колбэк из схемы страницы вернёт значение 10:
function mymodule_foobar_page($argument) { return $argument; }
Если вы хотите, чтобы в качестве переменного значения могли использоваться только числовые значения, а на любые другие Друпал говорил, что страница не найдена, то функцию mymodule_foobar_page() необходимо начать вот с такого кода:
function mymodule_foobar_page($argument) { if (!ctype_digit($argument)) { // Если переменный агрумент не цифра, // то отдаём заголовок 404 (страница не найдена) drupal_not_found(); } else { // Если цифра - продолжаем $output = 'Цифра ' . $argument; return $output; } }
Теперь об интересной фишке хука меню. Он умеет загружать объекты автоматически. Например, имеется такая схема:
function mymodule_menu() { $items = array(); $items['foo/%bar'] = array( 'page callback' => 'mymodule_foobar_page', 'page arguments' => array(1), ); return $items; }
Здесь переменное значение я указал не просто символом %, а %bar. В принципе, если это было сделанно не зная возможности автоматической загрузки, то вы о нём даже не догадаетесь - страница будет работать точно так же, как раньше. Однако если в модуль вы добавите функцию bar_load(), то в функцию mymodule_foobar_page() придёт не значение из урла, а результат выполнения bar_load'a.
Рассмотрим пример автоматической загрузки на примере нод. Создаём страницу, на которой будет отображаться материал:
function mymodule_menu() { $items = array(); $items['foo/%node'] = array( 'page callback' => 'mymodule_foobar_page', 'page arguments' => array(1), ); return $items; }
Обратите внимание, здесь в качестве переменного аргумента я указал %node. Теперь, если вы зайдёте на страницу, к примеру, foo/10, то в функцию mymodule_foobar_page() будет передано не число 10, а загруженный объект ноды с ID 10. А всё потому, что ядро Друпала уже включает в себя функцию node_load(). В качестве аргумента ей передаётся значение, указанное в page arguments - и нода загружается. После чего передаётся в функцию из page callback'a.
Заголовок страниц
Теперь давайте поговорим о заголовке страницы. Он указывается в ключе title и для него срабатывает title callback, который по умолчанию вызывает для title функцию t(). Вот эти два куска кода идентичны:
function mymodule_menu() { $items = array(); $items['foo/%node'] = array( 'title' => 'Node title', 'page callback' => 'mymodule_foobar_page', 'page arguments' => array(1), ); return $items; }
и
function mymodule_menu() { $items = array(); $items['foo/%node'] = array( 'title' => 'Node title', 'title callback' => 't', 'page callback' => 'mymodule_foobar_page', 'page arguments' => array(1), ); return $items; }
Именно по этой причине при создании страницы в title не нужно пихать функцию t(). Она вызовется и без вашего участия.
Если вы хотите, чтобы заголовок задавался в зависимости от переменного аргумента, то стоит воспользоваться ключом title arguments:
function mymodule_menu() { $items = array(); $items['foo/%node'] = array( 'title callback' => 'mymodule_title_callback', 'title arguments' => array(1), 'page callback' => 'mymodule_foobar_page', 'page arguments' => array(1), ); return $items; }
В этом случае в функцию mymodule_title_callback() будет передан объект ноды (и мы уже знаем, почему). Вам останется вернуть лишь его заголовок:
function mymodule_title_callback($node) { return $node->title; }
Абсолютно идентичные вызовы функций с параметрами происходит для следующих ключей:
- access callback и access arguments (проверка доступа на просмотр текущей страницы).
- theme callback и theme arguments (вызов функции темизации для рендера контента страницы).
Типы страниц
Страницы в Друпале бывают 6 типов:
- MENU_NORMAL_ITEM: страница, которая будет являться одним из пунктов системного меню.
- MENU_CALLBACK: обычная страница, ни к чему не привязанная.
- MENU_SUGGESTED_ITEM: страница, "предлагаемая" модулями.
- MENU_LOCAL_ACTION: локальными действиями являются страницы, которые описывают действия для родительской страницы. Пример - добавление нового пользователя или блока.
- MENU_LOCAL_TASK: Страница, которая будет являться табом. Пример - табы Редактировать и Просмотр в полном представлении ноды.
- MENU_DEFAULT_LOCAL_TASK: Таб по умолчанию (тот, который будет открываться первым). Например, для полного представления ноды - это таб Просмотр
Пример создания вкладок первого и второго уровня:
/** * Implements hook_menu(). */ function mymodule_menu() { $items = array(); $items['foo'] = array( 'title callback' => 'Основная страница', 'page callback' => 'mymodule_main_page', 'access callback' => TRUE, ); // Создаём первый таб первого уровня, который будет автоматически // открываться при переходе на страницу foo $items['foo/bar'] = array( 'title' => 'Заголовок 1', 'type' => MENU_DEFAULT_LOCAL_TASK, 'weight' => 0, ); // Второй таб первого уровня, который будет виден на странице foo $items['foo/bar2'] = array( 'title' => 'Заголовок 2', 'page callback' => 'mymodule_bar2_page', 'access callback' => TRUE, 'weight' =>1, 'type' => MENU_LOCAL_TASK, ); // Первый таб второго уровня для таба foo/bar $items['foo/bar/abc'] = array( 'title' => 'Заголовок 1.1', 'page callback' => 'mymodule_bar_abc_page', 'access callback' => TRUE, 'weight' => 0, 'tab parent' => 'foo/bar', 'type' => MENU_DEFAULT_LOCAL_TASK, ); // Второй таб второго уровня для таба foo/bar $items['foo/bar/abc2'] = array( 'title' => 'Заголовок 1.1', 'page callback' => 'mymodule_bar_abc2_page', 'access callback' => TRUE, 'weight' => 0, 'tab parent' => 'foo/bar', 'type' => MENU_DEFAULT_LOCAL_TASK, ); return $items; }
Такой структурой была задана схема страниц, состоящая из двух первичных табов для страницы foo, и два вторичных таба, видимых при активном табе foo/bar.
Из основного - это всё. Я рассказал не про все возможности схемы страниц в Друпале. Существует ещё несколько дополнительных ключей, о которых я не упомянул. К ним относятся:
- 'description' - описание страницы
- 'delivery callback' - функция, которая обрабатывает страницу и отдаёт её браузеру. По умолчанию это drupal_deliver_html_page()
- 'file' - название файла, в котором находятся колбэки из данной схемы страниц
- 'file path' - путь, где располагается файл с колбэками
- 'load arguments' - массив аргументов, которые передаются для автоматической загрузки. Он используется для случаев, когда вызов автоматической загрузки не является явным (например, в урле используется не %node, а просто %)
- 'weight' - определяет позицию страницы по отношению к другим. Например, для табов он определяет, какая страница будет первым табом, вторым и т.д.
- 'menu_name' - задаёт заголовок в меню, если страница была в него помещена.
- 'context' - описывает контекст появления страницы. Бывает двух типов - MENU_CONTEXT_PAGE (по умолчанию) и MENU_CONTEXT_INLINE.
- 'tab_root' - для страниц типа MENU_LOCAL_TASK и MENU_DEFAULT_LOCAL_TASK определяет ближайшую родительскую страницу, которая не является табом.
- 'position' - позиция блока на странице с администрированием блоков. Бывает 'left' или 'right'.
- 'options' - массив параметров, передаваемых в функцию l() для ссылки на страницу из меню.
- Spleshka
- 14.11.2011
- 16011
Комментарии
Здорово, спасибо за описания. Сейчас некоторые люди изучают фреймворки, микрофреймворки чтобы сократить рабочий процесс, но никто никогда не смотрит на друпал как на фреймворк *или микро-фреймворк. Недавно смотрел YII, как там делаются формы и другое, скажу что очень похоже на друпал, плюс многое не предусмотренно что есть тут. Поистине это необычный гибкий фреймворк слеюущий "философии" KISS)
Огромное спасибо! Лучший
сайтблог про Друпал!Хорошо что про семерку пишите, про нее мало инфы в интернете
А вы уверены, что в примере создания вкладок первого и второго уровня у вас все правильно? У меня как-то не выполняется 'page callback' в табах второго уровня, когда 'type' стоит в MENU_DEFAULT_LOCAL_TASK.
У меня еще возник вопрос в процессе работы с табами. Если например взять ваш пример с табами и в табах второго уровня выставить 'access arguments' => array('abc') и 'access arguments' => array('abc2') то есть определить hook_permission(), а в админке выставить одному табу полный доступ, а другому закрыть доступ, то в итого ни один таб не отображаются? В остальных случаях работает корректно.
function hook_permission()
{
return array(
'abc' => array( 'title' => t('abc') ),
'abc2' => array( 'title' => t('abc2') ),
);
}
Благодарю за комментарий! Действительно нужно убрать дефаулты у вкладок второго уровня.
Спасибо, очень помогли мне!
Комментировать