1С Битрикс · 7 мин чтения

Меню в Битрикс: файл .menu.php, типы меню и свой шаблон вывода

Меню в Битрикс редко ограничивается визуальным редактором в админке. Как только нужен третий уровень вложенности, отдельная верстка для футера или иконки у пунктов, приходится открывать файл .menu.php и разбираться с шаблоном компонента bitrix:menu. За несколько лет работы с 1С-Битрикс я правил такие файлы десятки раз: то заказчик просит развести меню шапки и левого сайдбара по разным типам, то нужно вывести пункты с картинками для мобильной версии. Разберу, как устроен .menu.php, какие типы меню есть в Битриксе из коробки и как написать свой шаблон вывода вместо стандартного .default.

Компонент bitrix:menu и файл .menu.php: как они связаны

Компонент bitrix:menu не хранит пункты меню в базе и не читает их напрямую из админки, если только для раздела не включена автогенерация (о ней ниже). Обычный источник данных для него - файл .menu.php, лежащий в той же папке, что и страница с меню, либо выше по дереву каталогов.
Поиск работает так: компонент берет параметр ROOT_MENU_TYPE, собирает по нему имя файла по формату .<тип меню>.menu.php (для ROOT_MENU_TYPE=“left” это .left.menu.php, для “top” - .top.menu.php) и ищет этот файл сначала в директории текущей страницы, потом поднимается на уровень выше, и так до корня сайта. Для раздела /ru/company/about/ последовательность такая: /ru/company/about/.left.menu.php, /ru/company/.left.menu.php, /ru/.left.menu.php, /.left.menu.php. Как только файл найден, поиск останавливается. Из-за этого достаточно положить один .left.menu.php в корне каталога, и все вложенные разделы без своего файла будут показывать то же меню, пока в конкретном подразделе не появится собственный .left.menu.php с более узким набором пунктов.
Если на сайте несколько языковых версий, для не дефолтного языка используется файл с суффиксом, например top.menu_en.php, и Битрикс сам выбирает нужный вариант по LANGUAGE_ID текущего сайта.

Типы меню в Битриксе: top, left, sitemap и собственные варианты

Из коробки визарды сайта обычно создают четыре стандартных типа, но список не закрытый: свой тип меню добавляют в настройках модуля «Управление структурой», после чего кладут файл .<тип>.menu.php в нужном разделе.

Тип меню Имя файла Где обычно используется
top .top.menu.php Верхняя навигация, разделы первого уровня
left .left.menu.php Меню каталога или раздела, второй-третий уровень
bottom / произвольный, например footer .bottom.menu.php или .footer.menu.php Подвал сайта, служебные ссылки
sitemap .sitemap.menu.php Карта сайта, полное дерево разделов

Пользовательский тип пригождается, когда шапка и подвал должны жить независимо друг от друга. Ставлю ROOT_MENU_TYPE=“footer” в вызове компонента, кладу рядом .footer.menu.php - и правка одного файла не задевает второй вызов меню на той же странице.

Структура записи в .menu.php построчно

Каждый пункт меню - это массив из пяти элементов, и порядок фиксирован:

  • 0 - текст ссылки, который увидит посетитель;
  • 1 - URL, относительный или абсолютный;
  • 2 - массив дополнительных ссылок, которые влияют на подсветку пункта, а не вложенное подменю;
  • 3 - произвольный ассоциативный массив дополнительных параметров (например, код иконки или флаг target=“_blank”), в шаблоне он приходит как $arItem[“PARAMS”];
  • 4 - условие отображения пункта: PHP-выражение, которое должно вернуть true, например CSite::InDir('/personal/') или CSite::InGroup(array(1,6)). Пустая строка означает, что пункт виден всегда.

Вот рабочий пример файла .top.menu.php. Массив плоский, вложенности внутри файла нет, а третий элемент у пункта «О компании» занят дополнительными ссылками для подсветки:

<?php
if (!defined("B_PROLOG_INCLUDED") || B_PROLOG_INCLUDED !== true) die();
$aMenuLinks = array(
    array(
        "Каталог",
        "/catalog/",
        array(),
        array(),
        ""
    ),
    array(
        "О компании",
        "/about/",
        array("/about/contacts/", "/about/vacancy/"),
        array(),
        ""
    ),
    array(
        "Доставка и оплата",
        "/delivery/",
        array(),
        array(),
        ""
    ),
);
?>

Данные для четвертого элемента удобно использовать под иконки: кладу туда array(“ICON” => “phone”), а в шаблоне читаю $arItem[“PARAMS”][“ICON”]. Это надежнее, чем городить пятый текстовый элемент под ту же задачу.

Бесплатный материал

🎁 Полезный скрипт в подарок

Подпишитесь на Telegram - пришлю готовый скрипт по этой теме.

Без спама. Отписка в 1 клик.

Автогенерация меню из структуры сайта и когда она мешает

В разделе управления структурой можно включить чекбокс, который заставляет Битрикс переписывать .menu.php автоматически при каждом сохранении раздела - система строит файл по реальному дереву страниц. Удобно, пока меню совпадает со структурой сайта один в один.
Проблема начинается, когда в файл руками добавлен пункт, которого нет среди разделов: внешняя ссылка, якорь на этой же странице или пункт с иконкой из PARAMS. Следующее сохранение через админку перезатирает файл целиком, и ручные правки пропадают без предупреждения. На практике для таких случаев я либо отключаю автогенерацию для конкретного раздела и веду .menu.php вручную, либо выношу нестандартные пункты в отдельный файл с собственным типом меню и вывожу его вторым вызовом компонента рядом с автогенерируемым меню.

Свой шаблон вывода меню вместо .default

Стандартный шаблон .default отдает голый ul/li без модификаторов, и под верстку из макета его почти всегда приходится менять. Свой шаблон кладу в /local/templates/ИМЯ_ШАБЛОНА/components/bitrix/menu/custom_top/template.php, а в вызове компонента указываю его вторым параметром.

<?php
$APPLICATION->IncludeComponent(
    "bitrix:menu",
    "custom_top",
    array(
        "ROOT_MENU_TYPE" => "top",
        "MAX_LEVEL" => "2",
        "CHILD_MENU_TYPE" => "left",
        "USE_EXT" => "Y",
        "DELETE_CHAIN" => "N",
        "ALLOW_MULTI_SELECT" => "N",
        "CACHE_TYPE" => "A",
        "CACHE_TIME" => "3600",
        "CACHE_SELECTED_ITEMS" => "N"
    ),
    false
);
?>

Параметр CHILD_MENU_TYPE стоит отдельного внимания: он позволяет верхнему меню на первом уровне подглядывать в файл другого типа (в примере - .left.menu.php) для построения выпадающего второго уровня, не создавая для этого отдельный top.menu.php с полным деревом.
Сам шаблон получает данные не через $arResult[“ITEMS”], а напрямую перебирает $arResult - массив плоский, а вложенность видна через DEPTH_LEVEL и SELECTED:

<?php
if (!defined("B_PROLOG_INCLUDED") || B_PROLOG_INCLUDED !== true) die();
?>
<ul class="main-menu">
<?php foreach ($arResult as $arItem): ?>
    <?php
    $active = $arItem["SELECTED"] ? " main-menu__item--active" : "";
    $icon = !empty($arItem["PARAMS"]["ICON"])
        ? '<i class="icon icon-' . htmlspecialcharsbx($arItem["PARAMS"]["ICON"]) . '"></i>'
        : "";
    ?>
    <li class="main-menu__item<?= $active ?>">
        <a href="<?= htmlspecialcharsbx($arItem["LINK"]) ?>"><?= $icon ?><?= htmlspecialcharsbx($arItem["TEXT"]) ?></a>
    </li>
<?php endforeach; ?>
</ul>

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

Кеширование и типичные проблемы с меню Bitrix

Самая частая жалоба «поменял .menu.php, а на сайте ничего не изменилось» почти всегда упирается в кеш компонента. Параметры CACHE_TYPE и CACHE_TIME в вызове bitrix:menu заставляют Битрикс хранить готовый HTML меню в кеше на весь указанный срок. На время отладки я временно ставлю CACHE_TYPE=“N”, а на проде после правок сбрасываю кеш в «Настройки - Настройки продукта - Автокеширование», на вкладке «Очистка файлов кеша», где среди вариантов есть отдельный пункт «Меню», а не жду, пока он истечет сам.
Вторая по частоте причина - CACHE_SELECTED_ITEMS=“Y”. Этот параметр кеширует вместе с версткой еще и то, какой пункт подсвечен активным, и на многостраничных разделах подсветка начинает залипать на первом закешированном варианте. Для меню с большим числом дочерних страниц ставлю здесь “N”.
Третья ситуация - меню внезапно пустое без единой ошибки в логе. Обычно это значит, что компонент ищет файл не там: не совпал ROOT_MENU_TYPE, файл лежит в соседней папке, а не в родительской по дереву, либо название файла набрано с опечаткой в языковом суффиксе. Если правки в такой конфигурации нужны на уже работающем сайте, а штатного программиста под рукой нет, я беру подобные задачи в рамках технической поддержки сайта - разбираю логику существующих меню и привожу файлы в порядок без пересборки шаблона целиком.

1С-Битрикс и PHP регулярно обновляются, поэтому перед внедрением я сверяюсь с актуальной документацией.

Частые вопросы

Как добавить третий уровень меню в Битриксе?

Вложенность внутри файла не задаётся: пункт это плоский массив из пяти элементов, а третий элемент отвечает за подсветку, а не за подменю. Уровни собираются из отдельных файлов меню в подразделах: в вызове компонента ставят ROOT_MENU_TYPE для первого уровня, CHILD_MENU_TYPE для остальных и уровень вложенности от одного до четырёх, а в каждом подразделе кладут файл меню дочернего типа, например .submenu.menu.php. Стандартные шаблоны bitrix:menu поддерживают до четырёх уровней, для большего числа уровней компонент копируют и правят.

Почему меню не обновляется после правки .menu.php?

Почти всегда дело в кеше компонента: ищите CACHE_TIME и CACHE_TYPE в параметрах bitrix:menu и сбрасывайте кеш в «Настройки - Настройки продукта - Автокеширование» на вкладке «Очистка файлов кеша», там есть отдельный вариант «Меню», а не ждите, пока он истечет сам. Второй по частоте вариант - вы правите не тот файл: компонент ищет .menu.php вверх по дереву папок и мог подхватить файл из родительского раздела.

Можно ли сделать разные меню для шапки и подвала на одном сайте?

Да, через разные ROOT_MENU_TYPE у двух вызовов компонента bitrix:menu на одной странице: например top для шапки и произвольный footer для подвала. Битрикс ищет .top.menu.php и .footer.menu.php отдельно друг от друга, поэтому правка одного файла не трогает второе меню.

Как вывести в меню иконку или дополнительный css-класс у пункта?

Нужные данные кладите в четвертый элемент записи .menu.php - это произвольный массив с любыми ключами. В шаблоне он приходит в $arItem[“PARAMS”], оттуда значение достается и оборачивается в htmlspecialcharsbx перед выводом класса, иконки или атрибута ссылки.

Есть задача?

Обсудим в мессенджере

Расскажите, что нужно сделать — отвечу в течение 4 часов в рабочее время. Первая консультация бесплатно.

Продолжая пользование настоящим сайтом Вы выражаете своё согласие на обработку Ваших персональных данных (файлов куки) с использованием Yandex.Metrika.
Понятно