Перейти к основному содержанию

Тематический слой Drupal 8

Самой очевидной частью системы тем Drupal является страница Appearance , на которой перечислены все темы, установленные на вашем сайте. Когда вы выбираете тему на странице администрирования Appearance, вы применяете определенный графический дизайн к данным вашего веб-сайта. Однако прикладная тема на самом деле является лишь небольшой частью всего тематического слоя.

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

В следующей главе мы обсудим, как ваш модуль должен интегрироваться со слоем темы. Мы поговорим об архитектуре системы, шаблонах, элементах рендеринга , предварительной обработке и переопределениях . 

Бизнес логика против логики представления


Итак, как лучше всего разметить наши данные и функциональность? Должны ли мы просто обернуть каждый фрагмент данных в HTML и вернуть это в виде гигантской строки? Как в следующем примере:

return '<div class="wrapper">' . $data . '</div>';

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

  • Чтобы сделать код проще в обслуживании.
  • Чтобы можно было легко менять реализацию одного уровня без необходимости перезаписывать другие слои.


Как мы упоминали во введении этой главы, тема по умолчанию, выбранная на странице «Внешний вид», является наиболее очевидной частью слоя темы. Также вы можете подумать, что тема отвечает за применение HTML и CSS для веб-сайта. Тем не менее, на drupal.org размещены тысячи модулей. Должна ли тема отвечать за разметку данных всех этих модулей? Очевидно нет.

Поскольку модуль наиболее близко знаком со своими собственными данными и функциональностью, он несет ответственность за обеспечение реализации темы по умолчанию. Пока модуль правильно использует систему тем, тема сможет переопределять любой HTML и CSS путем замены собственной реализации для реализации модуля.

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

В предыдущей главе мы увидели пример тематики в действии при создании списка HTML:

// Return the output in a Render array
    $build = array(
      '#theme'	=>	'item_list',
      '#items'	=>	$list,
      '#list_type'	=>	'ol'
    );

Эта структура называется массивом Render, который в конечном итоге передается службе рендеринга Drupal, которая фактически отображает HTML. Передав этот массив службе рендеринга , мы, по сути, просим Drupal разметить наши данные как item_list , используя элементы списка в массиве $ list, и мы также указываем, что хотим вернуть упорядоченный список, указав #list_type как ol , 

 

Для получения списка всех доступных функций темы в ядре Drupal 8 (и соответствующих документов) перейдите по ссылке https://api.drupal.org/api/drupal/core%21lib%21Drupal%21Core%21Render%21theme.api. PHP / группы / Themeable / 8.2.x .


С этого момента API-интерфейс Render отвечает за обработку и возврат выходных данных HTML стандартным способом. При разработке функциональности, которая должна возвращать HTML для отображения на сайте Drupal, вам нужно использовать приведенный выше шаблон и возвращать массивы Render в своем коде. Есть несколько причин, по которым вам следует это делать, но самая важная из них заключается в следующем: тематический слой Drupal был разработан таким образом, чтобы темы могли полностью контролировать любой вывод HTML на вашем сайте. Преимущество этого состоит в том, что шаблоны UX не будут различаться в разных областях вашего сайта, просто потому, что элементы HTML (например, списки, блоки, виджеты и т. Д.) Управляются глобальными шаблонами, которые ваша активная тема может переопределять .

 

hook_theme ()


В Drupal 8 весь выводимый рендеринг регистрируется через реализацию hook_theme () и управляется соответствующим файлом шаблона . 

Hook_theme () реализация должна быть помещена в .module файл и в первую очередь отвечает за определение несколько вещей, следующим образом :

Тема перехватывает, то что зарегистрировано в модуле
Значения переменных по умолчанию для хуков темы
Если хук ожидает элемент рендеринга вернуть его вместо переменных


Реализация hook_theme () модуля просто должна вернуть массив хуков тем, которые он создает. Например, давайте посмотрим на реализацию модуля Paragraphs:

/**
 * Implements hook_theme().
 */
function paragraphs_theme() {
  return array(
    'paragraph' => array(
      'render element' => 'elements',
    ),
    'paragraphs_dropbutton_wrapper' => array(
      'variables' => array('children' => NULL),
    ),
    'paragraphs_info_icon' => [
      'variables' => [
        'message' => NULL,
        'icon' => NULL,
      ],
    ],
    'paragraphs_add_dialog' => [
      'render element' => 'element',
      'template' => 'paragraphs-add-dialog',
    ],
    'paragraphs_actions' => [
      'render element' => 'element',
      'template' => 'paragraphs-actions',
    ],
  );
}

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

Реализация hook_theme (), определит новую функцию темы , т.е., он определит, что тематический слой Drupal может сделать при отображении данных на нашем сайте. Если бы мы фактически использовали один из описанных выше хуков тем, скажем,  paras_info_icon, мы бы просто вернули следующий массив Render в нашем коде:

$my_output['icon'] = [
  '#theme' => 'paragraphs_info_icon',
  '#message' => t('A message that appears on the site.'),
  '#icon' => 'icon_name',
];

Как вы можете видеть выше, мы указали, что этот массив Render будет использовать хук темы для отображения данных, используя ключ #theme . Затем мы определили переменные, которые использует этот конкретный хук темы, и если вы посмотрите на функцию paras_theme () в предыдущем фрагменте, вы увидите, что дополнительные ключи, которые мы передаем массиву Render, #message и # значок , которые определены там, под  массивом переменных . Поэтому нам просто нужно использовать этот шаблон при использовании хуков тем.

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

 

Файлы шаблонов


Шаблоны - это файлы, в основном содержащие HTML и синтаксис Twig, с использованием переменных шаблона. Вместо того, чтобы объявлять функцию theme_hook_name () (которая раньше была основным решением в предыдущих версиях Drupal), вместо этого модуль создаст файл hook-name.html.twig . Ниже приведено содержимое типичного файла шаблона typ-hook.html.twig :

<article{{ attributes.addClass(classes) }}>

  {{ title_prefix }}
  {% if not page %}
    <h2{{ title_attributes }}>
      <a href="{{ url }}" rel="bookmark">{{ label }}</a>
    </h2>
  {% endif %}
  {{ title_suffix }}

  {% if display_submitted %}
    <footer class="node__meta">
      {{ author_picture }}
      <div{{ author_attributes.addClass('node__submitted') }}>
        {% trans %}Submitted by {{ author_name }} on {{ date }}{% endtrans %}
        {{ metadata }}
      </div>
    </footer>
  {% endif %}

  <div{{ content_attributes.addClass('node__content') }}>
    {{ content }}
  </div>

Twig синтаксис

В случае , если вы не знакомы с синтаксисом Twig есть руководство , которое можно найти  здесь .
В предыдущем примере показано практически все, что вы, вероятно, увидите в файле шаблона. Они заключаются в следующем:

  • Печать переменной, содержащей строку
  • Условные операторы if / if not / else
  • Печать элементов рендеринга


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

До сих пор мы узнали, как регистрировать новые хуки тем, как возвращать их, используя массивы рендеринга, и как может выглядеть типичный файл шаблона. Как насчет передачи данных в файл шаблона? Давайте посмотрим на предварительную обработку .

 

Функции предварительной обработки


Функции препроцессора позволяют нам добавлять новые или изменять существующие переменные определенного хука темы. В свою очередь, эти переменные будут переданы в соответствующий файл шаблона хука темы.

Давайте рассмотрим практический пример, чтобы выяснить, как работают функции предварительной обработки. Сначала мы рассмотрим реализацию hook_theme () основного модуля block_content :

/**
 * Implements hook_theme().
 */
function block_content_theme($existing, $type, $theme, $path) {
  return [
    'block_content_add_list' => [
      'variables' => ['content' => NULL],
      'file' => 'block_content.pages.inc',
    ],
  ];
}

Вы можете видеть выше, что модуль block_content регистрирует новый обработчик тем , называемый block_content_add_list , передавая одну переменную с именем content и указывая ключ файла, который указывает на отдельный включаемый файл, содержащий код, связанный с темой.

Этот файл на самом деле содержит функцию предварительной обработки:

<?php

/**
 * @file
 * Provides page callbacks for custom blocks.
 */

use Drupal\Core\Url;

/**
 * Prepares variables for a custom block type creation list templates.
 *
 * Default template: block-content-add-list.html.twig.
 *
 * @param array $variables
 *   An associative array containing:
 *   - content: An array of block types.
 *
 * @see block_content_add_page()
 */
function template_preprocess_block_content_add_list(&$variables) {
  $variables['types'] = [];
  $query = \Drupal::request()->query->all();
  foreach ($variables['content'] as $type) {
    $variables['types'][$type->id()] = [
      'link' => \Drupal::l($type->label(), new Url('block_content.add_form', ['block_content_type' => $type->id()], ['query' => $query])),
      'description' => [
        '#markup' => $type->getDescription(),
      ],
      'title' => $type->label(),
      'localized_options' => [
        'query' => $query,
      ],
    ];
  }
}

Структура функции предварительной обработки на самом деле очень проста.

Создайте функцию, используя шаблон template_preprocess_ {theme hook} . В этом случае имя функции - template_preprocess_block_content_add_list, потому что оригинальный хук темы называется  block_content_add_list .
Функция получает единственный параметр & $ variable, который содержит массив доступных переменных для заданного хука темы.
Добавление элементов массива в & $ variable массива


В свою очередь, любые переменные, которые вы добавляете в массив & $ variable , станут доступны в файле шаблона для данного хука темы. Эта конкретная функция предварительной обработки добавляет только одну переменную с именем types . Давайте посмотрим на содержимое block-content-add-list.html.twig, чтобы увидеть эту переменную в действии:

{#
/**
 * @file
 * Default theme implementation to present a list of custom block types.
 *
 * Available variables:
 * - types: A collection of all the available custom block types.
 *   Each block type contains the following:
 *   - link: A link to add a block of this type.
 *   - description: A description of this custom block type.
 *
 * @see template_preprocess_block_content_add_list()
 *
 * @ingroup themeable
 */
#}
{% spaceless %}
  <dl>
    {% for type in types %}
      <dt>{{ type.link }}</dt>
      <dd>{{ type.description }}</dd>
    {% endfor %}
  </dl>
{% endspaceless %}

Как вы можете видеть выше, переменная types становится доступной в шаблоне Twig, включая все его подэлементы, такие как link и description , как определено в функции preprocess. С этого момента вы можете работать с  типами так же, как с любой другой переменной.

Переопределение функций предварительной обработки


Drupal также позволяет нам переопределять существующие функции предварительной обработки, либо изменять существующие переменные хука тем, либо добавлять новые, чтобы они стали доступны в файлах шаблонов. Переопределение может произойти на двух уровнях:

уровень темы : вы будете выбирать это вариант в 99% случаях при работе с функциями предварительной обработки
уровень модуля : вам нужно использовать его только тогда, когда функциональность вашего модуля требует изменения заданной функции предварительной обработки
Чтобы переопределить существующую функцию препроцессора или перехват темы, вам нужно поместить функцию, используя шаблон {ваша тема} _preprocess_ {перехват темы}, в файл темы .theme . Этот файл действует как файл .module для модуля, но только для тем. Любая связанная с темой реализация хуков должна быть помещена в этот файл, чтобы Drupal мог автоматически ее распознать и выполнить.

Как указано выше, также можно поместить переопределяющую функцию предварительной обработки в файл .module . В этом случае нужно использовать шаблон имени функции {module} _preprocess_ {theme hook}.
Если вы хотите переопределить функцию  template_preprocess_block_content_add_list , нужно добавить функцию с именем mytheme_preprocess_block_content_add_list в файл mytheme.theme. В этой функции мы просто добавляем новые элементы в массив $ variable, чтобы они стали доступны для слоя темы:

/**
 * Implements hook_preprocess_hook()
 */
function mytheme_preprocess_block_content_add_list(&$variables) {
  $variables['my_variable'] = get_my_variable();  
}

Хотя этот конкретный пример не очень полезен на практике, вы поняли идею. Просто реализуйте функцию с именем {theme} _preprocess_ {theme hook} и добавьте ваши новые переменные в массив $ variable .

Вы также можете использовать тот же механизм для изменения существующих переменных. В этом случае просто переопределите элемент $ variable [{variable}] массива.

Переопределение файлов шаблонов


Гибкий слой тем Drupal также позволяет нам очень просто переопределять существующие файлы шаблонов. Как мы уже узнали в этой главе, каждый хук темы имеет соответствующий шаблон Twig в системе. Как и в предыдущем примере, разметка хука темы block_content_add_list предоставляется block-content-add-list-html-twig .

Если вы хотите переопределить разметку этого шаблона, все, что вам нужно сделать, это скопировать этот файл в папку шаблонов вашей темы и очистить кеши. С этого момента Drupal будет распознавать и использовать этот файл шаблона, чтобы обеспечить вывод для данного хука темы.

Визуализация элементов


Элементы рендеринга были представлены в слое тем Drupal 7. Они существовали еще с Drupal 4.7 как часть API формы, но теперь они внедрены в сердце системы тем. Элемент Render - это сложная структура данных, передаваемая в качестве одного из параметров в drupal_render () как один параметр. Элементы рендеринга - это вложенные массивы, которые могут включать в себя:

Данные для визуализации
Другие элементы рендеринга, которые считаются «дочерними» для элемента
Массив структур, таких как файлы CSS и JavaScript, которые должны быть прикреплены к странице при ее отображении
Список хуков тем, которые можно использовать для тематических данных
Список функций обратного вызова для запуска элемента до и после его тематического
Давайте посмотрим на простой элемент рендеринга:

$element = array(
  '#prefix' => '<div class="plain">',
  '#markup' => '<p>' . t('Very simple text here.') . '</p>',
  '#suffix' => '</div>',
);

Это простой пример, но когда мы начнем рассматривать более сложные элементы рендеринга, мы увидим, что каждый ключ массива в элементе рендеринга может быть одним из трех:

Свойство элемента рендеринга. Это префикс # .
Дочерний элемент. Все ключи массива без префикса # считаются дочерними элементами.
Переменная для передачи в функцию темы. В визуализации имена переменные начинаются с # (так же , как свойства), drupal_render ()  уберет # от имени перед отправкой выводом.
Имея это в виду, давайте посмотрим на немного более сложный рендер-массив:

$element = array(
  '#prefix' => '<div class="less-simple">',
  '#suffix' => '</div>',
  'user' => array(
    '#theme' => 'username',
    '#account' => $account,
  ),  
  'separator' => array(
    '#markup' => '<br />',
  ),
  'logout' => array(
    '#type' => 'link',
    '#title' => t('Log out'),
    '#url' => Url::fromRoute('user.logout')
  ),  
);

Во-первых, мы должны определить дочерние элементы, так как их проще всего обнаружить. user , separator и logout  являются дочерними элементами нашего массива рендеринга. Сепаратор дочерний элемент представляет собой другой пример простого #markup элемента.

Глядя на элемент user , мы видим, что его свойство #theme установлено в username . drupal_render () возьмет этот дочерний элемент и передаст его _ theme () с хуком темы username ;

И наконец, свойство #type элемента logout установлено на ссылку . Свойство #type сообщает drupal_render (), как визуализировать этот элемент. В этом случае мы хотим отобразить этот элемент как ссылку, поэтому мы устанавливаем тип для ссылки, а также определяем требуемое  свойство #url . В свою очередь drupal_render () отобразит этот элемент как HTML-ссылку.

Render свойства


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

#type : Строка, указывающая, какой тип элемента отображается. Свойства по умолчанию для этого типа элемента извлекаются из данных, указанных в плагине RenderElement (подробнее об этом через минуту).


#markup : строка, содержащая разметку (например, HTML). Если это свойство используется, свойство #type указывать не нужно, так как оно будет автоматически установлено как разметка .


#cache : массив, указывающий, должен ли элемент извлекаться из кэша или сохраняться в кэше после рендеринга.


#theme : Строка, определяющая хук темы, который будет использоваться в элементе.


#pre_render : Массив обратных вызовов для применения к элементу перед его выводом в теме.


#post_render : Массив обратных вызовов для применения к элементу после создания тем.


#children : визуализированный элемент и его дочерние элементы. Обычно он создается внутренне с помощью drupal_render (), поскольку он отображает элементы, но также может быть установлен с помощью обратного вызова #pre_render .


#prefix : строка, содержащая разметку, которая должна быть добавлена ​​к выводу.


#suffix : строка, содержащая разметку, которая будет добавлена ​​к выводу.


#attached : массив библиотек или других связанных вложений, связанных с элементом.


Есть еще много свойств, которые могут быть использованы в массиве рендеринга. Для получения полного списка элементов рендеринга и их соответствующих свойств, определенных ядром Drupal, перейдите по  ссылке https://api.drupal.org/api/drupal/elements .


Определение новых элементов рендеринга


Как разработчик, вы также можете создавать свои собственные элементы рендеринга, которые вы можете использовать в своем коде. Для этого вам нужно создать новый плагин с помощью RenderElement или FormElement аннотации и расширить RenderElement или FormElement класс, соответственно, и поместить его в папку с названием  SRC / Element в каталоге вашего модуля.

Более подробно мы рассмотрим новую систему плагинов и аннотированные классы Drupal в следующей главе.


Резюме


Эта несколько более короткая глава была сосредоточена на слое тем Drupal 8 где мы рассмотрели некоторые из его основных концепций, таких как массивы рендеринга, хуки тем и файлы шаблонов. Мы также затронули Twig, новый шаблонный движок Drupal по умолчанию. Мы также изучили функции и механизмы предварительной обработки для переопределения хуков тем и файлов шаблонов.

В следующей главе мы рассмотрим одну из самых крутых новых возможностей Drupal 8: управление конфигурацией.

Теги
Top