УрокРендер массивов в седьмом Друпале

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

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

Что такое рендер?

Касательно Друпала, рендер обозначает преобразование рендер массивов в HTML код.

Что такое рендер массив?

Рендер массив – классический структурированный массив в Drupal, который предоставляет информацию/данные (возможно, вложенную) наряду с подсказками относительно того, как этот элемент должен быть представлен (например, #type определяет тип элемента).

Массив страницы может выглядеть следующим образом:

$page = array(
  '#show_messages' => TRUE,
  '#theme' => 'page',
  '#type' => 'page',
  'content' => array(
    'system_main' => array(...),
    'another_block' => array(...),
    '#sorted' => TRUE,
  ),
  'sidebar_first' => array(
    ...
  ),
  'footer' => array(
    ...
  ),
  ...
);

Для чего это делается?

До выхода Друпал 7 можно было запросто изменять формы (с помощью hook_form_alter()), однако остальные элементы, которые можно было изменять в модулях или темах, превращались в HTML код до того, как передавались для изменений.

Начиная с седьмого Друпала модуль или тема может использовать hook_page_alter(), чтобы изменить структуру или содержание страницы в самый последний момент. Это означает, что несколько строк PHP кода в теме могут разместить блок в том месте страницы, где вам это необходимо.

Изменение элементов

После выхода седьмого Друпала блоки и страницы могут быть изменены так же, как раньше мы меняли формы. Многие другие элементы также стали доступны для изменения. Например, с помощью hook_page_alter() вы можете в своём модуле или теме сделать так:

function ИМЯМОДУЛЯ_page_alter(&$page) {
  // Пененосим форму поиска из левой колонки в футер
  $page['footer']['search_form'] = $page['sidebar_first']['search_form'];
  unset($page['sidebar_first']['search_form']);
 
  // Убираем блок "Powered by Drupal"
  unset($page['footer']['system_powered-by']);
}

Как рендерные массивы связаны с элементами?

В модулях можно определять новые элементы, которые на самом деле являются рендерными массивами, записанными по определённым правилам. Для этого используется hook_element_info(). Элемент – это рендерный массив, который имеет #type и другие свойства, определяющие его дальнейшее поведение. Когда создаётся рендерный массив и ему задан #type, то к нему подгружаются значения по умолчанию для тех атрибутов, которых нет в массиве, но они описаны в hook_element_info() для этого элемента.

Создание данных в виде рендерных массивов

В Drupal 7, в отличие от прошлых версий, модуль практически всегда создает данные в виде рендерных массивов. Функция вызова любой страницы возвращает массив, который в дальнейшем будет отрендерен (превращён в HTML). Это позволяет модулям хранить данные в массивах настолько долго, насколько это возможно в процессе создания страницы.

В Drupal 6 использовался метод рендеринга данных и их сбора в виде HTML:

function mymodule_menu() {
  $items['mypage-html'] = array(
    'title' => 'Страница с функцией возвращающей HTML',
    'page callback' => 'mymodule_html_page',
    'access callback' => TRUE,
  );
 
  $items['mypage-ra'] = array(
    'title' => 'Страница возвращающая рендерный массив',
    'page callback' => 'mymodule_ra_page',
    'access callback' => TRUE,
  );
 
  return $items;
}
 
// Старый метод, который возвращает html код.
// Кстати, он ещё работает в седьмом Друпале. 
function mymodule_html_page {
  $output = '<p>A paragraph about some stuff...</p>';
  $output .= '<ul><li>first item</li><li>second item</li><li>third item</li></ul>';
  return $output;
}
 
// Новый метод, который возвращает рендерный массив
function mymodule_ra_page {
  $output =  array(
    'first_para' => array(
      '#type' => 'markup',
      '#markup' => '<p>A paragraph about some stuff...</p>',
    ),
    'second_para' => array(
      '#items' => array('first item', 'second item', 'third item'),
      '#theme' => 'item_list',
    ),
  );
  return $output;
}

Примеры массивов разных типов

Как и раньше, все элементы Drupal (см. hook_element_info(), который был hook_elements() в Drupal 6) представляют собой описанный тип данных. Глядя на system_element_info(), мы видим множество предопределенных типов, в том числе page, form, html_tag, value, markup, link, fieldset и многие другие. По соглашению, #- атрибуты, используемые этим типом, документируются с соответствующей функцией темизации. Таким образом, например, атрибуты, используемые в элементе типа #type => 'html_tag' можно найти в функции theme_html_tag().

Вот три примера из модуля Examples (Render example):

$demos = array(
  t('Super simple #markup')  => array(
    '#markup' => t('Some basic text in a #markup (shows basic markup and how it is rendered)'),
  ),
 
  'prefix_suffix' => array(
    '#markup' => t('This one adds a prefix and suffix, which put a div around the item'),
    '#prefix' => '<div><br/>(prefix)<br/>',
    '#suffix' => '<br/>(suffix)</div>',
  ),
 
  'theme for an element' => array(
    'child' => array(
      t('This is some text that should be put together'),
      t('This is some more text that we need'),
    ),
    '#separator' => ' | ',
    '#theme' => 'render_example_aggregate',
  ),
);

Примеры типов элементов

К рендерному массиву можно применить множество уже существующих типов или создать свои. Ниже приведены самые распространенные.

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

#type Тип элемента. Если массив – элемент, то свойства элемента будут загружены по умолчанию. Это ведет к сокращению набора предопределенных свойств, которые могут быть взяты из описания этого элемента в hook_element_info().
#markup Простейшее свойство, которое просто создает строку с разметкой #type => 'markup'. По сути, с его помощью можно просто вставить кусок HTML’a без каких-либо преобразований или дальнейшего рендера.
#prefix/#suffix Используется при необходимости добавить строку (или разметку) в начале или в конце элемента.
#pre_render Массив функций, который может изменить рендерный массив перед вызовом рендера для него. Это свойство может изменить, удалить части, установить #printed = TRUE, чтобы предотвратить последующий рендер и т.д.
#post_render Массив функций, который может обрабатывать HTML, полученный в результате рендера элемента. По большому счету это тоже самое, что и #theme_wrappers, за исключением того, что не используется система темизации.
#theme Набор хуков темы, которые полностью ответственны за рендер элементов массива (включая дочерние).
Примечание: на самом деле, #theme в Drupal 7 и #theme в Drupal 6 не связаны между собой.
Обычно, #theme' => 'function_name' вызывает функцию вида theme_function_name(), а остальные значение массива передаются в эту функцию в качестве аргументов.
Вот список всех дефолтных хуков темы: http://api.drupal.org/api/drupal/modules--system--theme.api.php/group/themeable/7
#theme_wrappers Массив функций темы, который имеет возможность добавлять что-либо к рендерному массиву после того, как дочерние элементы были отрендерены и помещены в #children.
Обычно используется для добавления HTML вокруг отрендереных дочерних элементов, особенно если дочерние элементы были отрендерены рекурсивно с помощью собственных функций темизации. Это свойство редко используют вместе с #theme.
#cache Обозначает массив как кэшируемый и определяет срок его хранения. Если рендерный массив уже был отрендерен, он больше не будет рендироваться, пока не закончится срок хранения кэша. При кэшировании используются стандартные функции Друпала cache_get() и cache_set().

Этот атрибут может содержать такой массив массив:

  • 'keys' => массив ключей, которые будут объеденены для создания ключа кэша.
  • 'bin' => имя кэша, которое будет использоваться (Например, 'cache' или 'cache_page' и т.д.)
  • 'expire' => дата, записанная в Unix-формате, определяющая срок годности кэша.
  • 'granularity' => значение, обозначающая тип кэша. Например, DRUPAL_CACHE_PER_PAGE, DRUPAL_CACHE_PER_ROLE или DRUPAL_CACHE_PER_USER.

Обратите внимание, что элементы, помеченные с атрибутом #cache будут хранить кэш до тех пор, пока не запустится крон, не смотря на его срок годности.

Комментарии

Аватар пользователя Liqor
Liqor написал:

Спасибо!)) Очень полезно!

15.02.2012 15:19
Аватар пользователя Андрей Гость
Андрей Гость написал:

Спасибо за статью! После 3-х дней мытарств с Друпал 7, понял что вся проблема в том, что я не понимаю этих массивов для рендеринга.

07.07.2012 01:47
Аватар пользователя Alexander Kruchinin
Alexander Kruchinin написал:

Замечательная статья (и блог) — всё внятно, по сути и наконец-то становится ясен механизм темизации.
Спасибо!

25.11.2012 17:25
Аватар пользователя м60тх996
м60тх996 написал:

преобразование рендер массивов в HTML код. - это называется визуализация

19.02.2013 20:38
Аватар пользователя NLD
NLD написал:

Хорошая статья! Только в начале возникает пара неясностей с терминами:

"Рендерный массив - это ассоциативный массив ..."
"Рендер массив – классический структурированный массив ..."

1. "Рендерный массив" и "рендер массив" - одно и тоже или разные вещи?
2. Чем отличаются друг от друга ассоциативный массив и классический структурированный массив?

20.11.2013 15:20

Комментировать