Элементы меню в Битрикс хранятся в специальных файлах с именем .тип_меню.menu.php. Динамические пункты, то есть те, которые выводятся из базы данных, в файлах .тип_меню.menu_ext.php.
Для вывода разделов инфоблока в меню есть компонент bitrix:menu.sections. А вот для вывода элементов, компонента нет.
Давайте выведем разделы и элементы, которые не прикреплены к разделам, в меню.
Хотим, чтобы пункты добавились выпадающими к основному пункту Каталог. Если нужный нам пункт ведет на страницу /catalog/, то и меню с выпадающими пунктами должно быть в этой папке.
Тип меню, который требуется создавать можно узнать в параметрах вызова компонента bitrix:menu. Смотрим значение CHILD_MENU_TYPE. Обычно это left.
Значит создаем файл "/catalog/.left.menu_ext.php".
В файле сделаем получение разделов через компонент bitrix:menu.sections и получение элементов через CIBlockElement::GetList(). Сделаем сразу кеширование, чтобы не нагружать базу данных лишними запросами. А чтобы кеш сбрасывался, когда мы добавляем или изменяем элементы в нашем инфоблоке, добавим теги к кешу. Кеш компонента bitrix:menu.sections выключим, так как у нас и так есть кеширование. В фильтре CIBlockElement::GetList укажем ID инфоблока, элементы брать только из корня и только активные в порядке сортировки.
Код файла .left.menu_ext.php:
<?
if(!defined("B_PROLOG_INCLUDED") || B_PROLOG_INCLUDED!==true)die();
global $APPLICATION;
use \Bitrix\Main\Data\Cache;
use \Bitrix\Main\Application;
$iblockId = 2;
$cache = Cache::createInstance();
$taggedCache = Application::getInstance()->getTaggedCache();
$cachePath = 'top_menu_iblock_' . $iblockId;
if ($cache->initCache(36000, $cachePath, $cachePath))
{
$aMenuLinksExt = $cache->getVars();
}
elseif ($cache->startDataCache())
{
$taggedCache->startTagCache($cachePath);
$aMenuLinksExt = $APPLICATION->IncludeComponent(
"bitrix:menu.sections",
"",
Array(
"ID" => "",
"IBLOCK_TYPE" => "products",
"IBLOCK_ID" => $iblockId,
"SECTION_URL" => "",
"DEPTH_LEVEL" => "1",
"CACHE_TYPE" => "N",
"CACHE_TIME" => "3600"
)
);
$res = CIBlockElement::GetList (
['SORT' => 'ASC'],
["IBLOCK_ID" => $iblockId, "IBLOCK_SECTION_ID" => false, 'ACTIVE' => 'Y'],
false,
false
);
$aMenuProducts = [];
while($element = $res->GetNext())
{
$aMenuLinksExt[] = [
$element['NAME'],
$element['DETAIL_PAGE_URL'],
[$element['DETAIL_PAGE_URL']],
[
'FROM_IBLOCK' => true,
'IS_PARENT' => false,
'DEPTH_LEVEL' => '1',
]
];
}
$taggedCache->registerTag("iblock_id_{$iblockId}");
$taggedCache->endTagCache();
$cache->endDataCache($aMenuLinksExt);
}
$aMenuLinks = array_merge($aMenuLinks, $aMenuLinksExt);
?>
У нас получилось в меню вначале идут разделы и после элементы. А что если мы хотим какие-то элементы выводить над ссылками разделов?
Тогда надо сделать сортировку полученных разделов и элементов между собой по полю SORT. Увы, стандартный bitrix:menu.sections не возвращает значение сортировки раздела. Придется копировать компонент в свое пространство.
Копируем содержимое папки /bitrix/component/bitrix/menu.sections в /local/component/my/menu.sections. В папке local в скопированном компоненте открываем файл component.php. Добавляем работу с сортировкой как на картинках.
Теперь изменим файл .left.menu_ext.php:
<?
if(!defined("B_PROLOG_INCLUDED") || B_PROLOG_INCLUDED!==true)die();
global $APPLICATION;
use \Bitrix\Main\Data\Cache;
use \Bitrix\Main\Application;
$iblockId = 2;
$cache = Cache::createInstance();
$taggedCache = Application::getInstance()->getTaggedCache();
$cachePath = 'top_menu_products';
if ($cache->initCache(36000, $cachePath, $cachePath))
{
$aMenuLinksExt = $cache->getVars();
}
elseif ($cache->startDataCache())
{
$taggedCache->startTagCache($cachePath);
$aMenuLinksExt = $APPLICATION->IncludeComponent(
"bitrix:menu.sections",
"",
Array(
"ID" => "",
"IBLOCK_TYPE" => "products",
"IBLOCK_ID" => $iblockId,
"SECTION_URL" => "",
"DEPTH_LEVEL" => "1",
"CACHE_TYPE" => "N",
"CACHE_TIME" => "3600"
)
);
$res = CIBlockElement::GetList (
[],
["IBLOCK_ID" => $iblockId, "IBLOCK_SECTION_ID" => false, 'ACTIVE' => 'Y'],
false,
false
);
$aMenuProducts = [];
while($element = $res->GetNext())
{
$aMenuLinksExt[] = [
$element['NAME'],
$element['DETAIL_PAGE_URL'],
[$element['DETAIL_PAGE_URL']],
[
'FROM_IBLOCK' => true,
'IS_PARENT' => false,
'DEPTH_LEVEL' => '1',
'SORT' => $element['SORT'],
]
];
}
usort($aMenuLinksExt, function($a, $b){
if (is_null($a[3]['SORT'])) $a[3]['SORT'] = 0;
if (is_null($b[3]['SORT'])) $b[3]['SORT'] = 0;
if ($a[3]['SORT'] === $b[3]['SORT'])
{
return 0;
}
return ($a[3]['SORT'] < $b[3]['SORT']) ? -1 : 1;
});
$taggedCache->registerTag("iblock_id_{$iblockId}");
$taggedCache->endTagCache();
$cache->endDataCache($aMenuLinksExt);
}
$aMenuLinks = array_merge($aMenuLinks, $aMenuLinksExt);
?>
Меняя сортировку разделов и элементов, мы можем переставлять пункты в меню. Если у элемента будет значение сортировки меньше, чем у раздела, он будет выше.