Drupal 8: Программный вывод меню

Программно выводим нужное меню в Drupal 8.

13.10.2014
12 комментариев
4 мин.

Если вам понадобится вывести меню программно, на странице или еще где, да даже просто получить пункты меню, то так просто как в Drupal 7 уже не выйдет. Вот два варианта вывода меню.

Стандартный
// ЗАМЕТКА: Получить список всех меню: menu_ui_get_menus();
// Импортируем необходимый класс.
use Drupal\Core\Menu\MenuTreeParameters;
// Создаем объект с настройками.
// Он имеет свои настройки, например минимальный и максимальный уровень
// вложения.
// Подробнее: https://api.drupal.org/api/drupal/core!lib!Drupal!Core!Menu!MenuTreeParameters.php/class/MenuTreeParameters/8
$menu_tree_parameters = new MenuTreeParameters();
// Загружаем меню с параметрами. Название меню здесь: main.
$tree = \Drupal::menuTree()->load('main', $menu_tree_parameters);
// Преобразуем меню в renderable array.
$tree_array = \Drupal::menuTree()->build($tree);
// Рендерим меню и получаем html ul список. Он будет сгенерирован с
// использованием шаблона по умолчанию для Drupal 8.
$menu = drupal_render($tree_array);
echo $menu; // вывод.

Продвинутый способ

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

Регистрируем шаблон
/**
 * Implements hook_theme().
 *
 * Объявляем наш темплейт через который будет прогоняться меню.
 * Тут ничего нет нового для тех кто ранее уже использовал данный хук.
 * Всё это на примере темы, использование в своём модуле может немного
 * отличаться.
 */
function MYTHEME_theme() {
  return array(
    // Объявляем название нашего темплейта.
    'my_custom_menu_template' =?> array(
      // Объявляем какие переменные принимает темплейт.
      'variables' => array(
        'items' => NULL,
      ),
      // Объявляем название файла-шаблона. html.twig указывать не нужно.
      'template' => 'my-custom-menu-template',
    ),
  );
}

Создаем шаблон

Теперь нам надо в папке templates создать шаблон * my-custom-menu-template.html.twig* со следующим содержимым.

{% import _self as menus %}

{{ menus.menu_links(items, attributes, 0) }}

{% macro menu_links(items, attributes, menu_level) %}
  {% import _self as menus %}
  {% if items %}
    {% if menu_level == 0 %}
      <ul>
    {% else %}<ul class="submenus"><li>
    {% endif %}
    {% for item in items %}
      </li><li item.attributes="">
        {{ link(item.title, item.url) }}
        {% if item.below %}
          {{ menus.menu_links(item.below, attributes, menu_level + 1) }}
        {% endif %}
      
    {% endfor %}
    </li>{{></ul>
  {% endif %}
{% endmacro %}
</ul>{{>

Это слегка модифицированный пример оригинального темплейта, который вы можете найти в /core/modules/system/templates/menu.html.twig. Тут для вложенного меню добавлен класс submenu.

Выводим меню

Вывод от стандартного будет отличаться лишь тем, что мы изменим шаблон.

use Drupal\Core\Menu\MenuTreeParameters;

$menu_tree_parameters = new MenuTreeParameters();
$tree = \Drupal::menuTree()-?>load('main', $menu_tree_parameters);
$tree_array = \Drupal::menuTree()->build($tree);
// Меняем тему на нашу.
$tree_array['#theme'] = 'my_custom_menu_template';
$menu = drupal_render($tree_array);
echo $menu; // вывод.

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

Drupal
Drupal 8

Комментарии

Саша   чт, 13/11/2014 - 00:37

Никита, а когда будет смысл заказывать сайт на 8-ке? Или, возможно, когда хорошие исполнители будут браться за 8-ку?

А если, например, мне нужно, чтобы через месяц исполнитель уже стартовал
проект, а на 8-ке ещё не реально, то сколько сайт сможет прожить на 7-ке? Год? Два? Три? Сайт по функционалу уровня рядового агентства недвижимости.

Niklan   чт, 13/11/2014 - 11:38

Привет.

На 8-ке делать сейчас НИКАКОГО смысла и причины нет. Я с 8-кой уже достаточно неплохо познакомился и могу с уверенностью сказать, даже если вы найдете исполнителя\студию которая вам сделает это, вам придется минимум одного разработчика брать в штат или на поддержку причем чуть ли, опять же, не постоянную. Постоянно придется править все, редактировать и т.д. А так как таких разрабов, кто знает 8-ку отлично, очень мало, и основная масса это разработчики ядра, то стоимость будет космическая. Делайте на 7-ке и не парьтесь, просто заказывайте так, чтобы вам делали максимально совместимо с 8-кой, чтобы было проще мигрировать в дальнейшем. Например, вместо node reference, использовать entity reference, который в ядре 8-ки и т.д. Уже многие модули для 7-ки пишутся и под 8-ку, старайтесь отдавать предпочтение подобным, ибо с большой вероятностью будет миграционный патч, ну как минимум модуль будет и можно ручками перенести. 7-ка будет поддерживаться около 3-ёх лет после релиза 8-ки, которая раньше лета 2015, скорее всего, не релизнится. 3 года для сайта это приличный срок, это отметка когда НУЖНО делать ребрендинг, если он не делался ранее, и все делать под текущие реалии. В этот момент и можно будет сьехать на 8-ку, либо сделать аналог с нуля (если контента не так много будет).

TL;DR - берите 7-ку и не рискуйте своими деньгами с 8-кой. 8-ку имеет смысл заказывать, как минимум, после релиза, а в лучшем случае через пол года после релиза. В общем эту затею можно смело оттягивать на 1 год.

P.s. Даже банально я, портанув Mappy на 8-ку нашел, как мне показалось, нерпавильную логику работы этой саймой 8-ки и API. Написал ишью, месяц никто там реагировал, а сейчас там статус Critical и проблему решают на уровне ядра, и это заденет API. В случае с найденной мной ошибкой, вероятнее всего контриб модулей это не коснется, решится на уровне ядра, а таких критикалов сейчас много и некоторые скорее всего еще сломают API и это доработки\переделки вашего сайта, а это доп. расходы, причем не маленькие.

Виктор   вт, 23/02/2016 - 11:20

Никита спасибо за статью. Буду благодарен если подскажете. На Drupal 8 я столкнулся с проблемой -
мне нужно в навигации по сайту в шапке, добавить по бокам кнопок, картинку. чтобы li в main_menu, были с двух сторон с картинками.
В Drupal 7 я взял за основу тему ZEN и это реализовал так -

а в Drupal 8 взял тему Basic нашел там файл menu.html.twig в нем попытался поменять шаблон. но когда стал менять у меня это стало влиять не только на верхнее меню, но и другие меню которые были на странице.
Не подскажете как сделать чтобы сделать шаблон только для Primary menu или main menu.

Виктор   вт, 23/02/2016 - 11:54

Я в принципе решил данную проблему отключив блок меню в футере, и кажется понял где была проблема. Но вот интерес и вопрос остался,- как можно реализовать несколько шаблонов меню для одной темы?

pr0g   ср, 18/05/2016 - 07:22

Привет!
А куда вставлять вывод, что написан в стандартном варианте и последний блок кода в продвинутом варианте? Ведь в 8-ке отсутствует модуль PHP Filter.
Спасибо.

chimir   вт, 13/12/2016 - 22:01

Никита, поправь код в статье, а то в нем множество артефактов

'my_custom_menu_template' =?> array(
  • {{> {{> \Drupal::menuTree()-?>load(
    Darkhan Kutzhanov   вт, 10/04/2018 - 07:52

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

    Niklan   вт, 10/04/2018 - 15:24

    Примерно так:

    use Drupal\Core\Url;
    
    /**
     * Implements hook_preprocess_HOOK().
     */
    function MYMODULE_preprocess_menu__main(&$variables) {
      $items = &$variables['items'];
    
      $items['custom_menu_entry'] = [
        'title' => 'Заголовок ссылки',
        'url' => Url::fromUri('https://google.ru/'),
        'below' => [],
      ];
    }
    
    Mykola Veriga   пт, 12/07/2019 - 16:10

    А как можно загрузить название меню? Здесь только список