Меню в Битрикс редко ограничивается визуальным редактором в админке. Как только нужен третий уровень вложенности, отдельная верстка для футера или иконки у пунктов, приходится открывать файл .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 перед выводом класса, иконки или атрибута ссылки.