Меню

Многоуровневое меню в Битрикс

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

Итак задача. Допустим у нас есть сайт, который имеет главную страницу, страницу контактов (/contacts/), раздел услуг (/services/) со статическими страницами (/services/serv1/, /services/serv2/ и т.д.) и каталог товаров (/catalog/) с разделами использующий SEF (/catalog/sect1/, /catalog/sect2/ и т.д.). Нам необходимо создать главное двухуровневое меню следующего вида:

Главная
Услуги
    Услуга 1
    Услуга 2
    Услуга 3
    Услуга 4
Каталог
    Раздел 1
    Раздел 2
    ....
    Раздел n
Контакты

Начнем с азов. Битрикс имеет встроенный механизм создания меню с любым количеством пунктов и уровней. Эти меню могут управляться как из административного раздела, так и из публичного раздела через интерфейс "Эрмитаж". Однако здесь мы эти способы рассматривать не будем.

Физически для построения меню используется компонент bitrix:menu, а также файлы меню находящиеся в соответствующих директориях сайта, содержащие описание пунктов и имеющие соответствующие названия. Например, .top.menu.php. Название всегда начинается с точки и оканчивается на .menu.php. А слово top в нашем примере говорит о типе меню. Различные меню имеют различные типы. Например, у нас есть верхнее, боковое и нижнее меню. Соответственно в разделе может быть до 3-х файлов меню. Для того чтобы компонент вывода меню понимал какой файл брать им присваиваются типы. Типы предварительно должны быть описаны в настройках модуля Управление структурой для каждого сайта отдельно.

При разработке системы меню необходимо четко понимать как должны файлы меню располагаться в физической структуре сайта. Меню всегда располагается в той папке, где оно будет выводиться. То есть если у нас есть левое одноуровневое меню типа left и мы переходим по адресу http://site.com/a/b/c/, то компонент вывода меню будет искать файл .left.menu.php в директории /a/b/c/. Если там файл не существует, то поиск переместится в /a/b/. И так далее до корневого каталога.

Что касается многоуровневого меню, то оно строится в сторону углубления в структуру сайта. Мы определяем основной тип меню и тип, из которого будут составляться дальнейшие уровни меню. Пусть в нашем случае это будут top и left. Компонент проходит по пунктам меню и если по физическому адресу ссылки будет найден файл .left.menu.php, то из него будет составлено подменю второго уровня, далее то же самое будет проделано и с ним до тех пор пока мы не достигнем заданного уровня вложенности.

Итак, перейдем к нашей задаче. В шаблоне сайта разместим код вызова компонента bitrix:menu.
<?$APPLICATION->IncludeComponent("bitrix:menu", ".default", array(
		"ROOT_MENU_TYPE" => "top",
		"MAX_LEVEL" => "2",
		"CHILD_MENU_TYPE" => "left",
		"USE_EXT" => "Y",
		"MENU_CACHE_TYPE" => "A",
		"MENU_CACHE_TIME" => "3600",
		"MENU_CACHE_USE_GROUPS" => "Y",
		"MENU_CACHE_GET_VARS" => ""
	),
	false,
	array(
		"ACTIVE_COMPONENT" => "Y"
	)
);?>
Вызов стандартный. Здесь ROOT_MENU_TYPE и CHILD_MENU_TYPE соответственно типы первого и последующих уровней меню. MAX_LEVEL - количество уровней. В нашем случае верхний и еще один. USE_EXT - использовать дополнительные файлы меню для разделов с динамически меняющимися пунктами. У нас есть каталог, поэтому ставим Y.

В корневой папке сайта создаем файл .top.menu.php:
<?
$aMenuLinks = [
	[
		"Главная",
		"/",
		[],
		[],
		"" 
	],
	[
		"Услуги",
		"/services/", 
		[],
		[],
		"" 
	],
	[
		"Каталог",
		"/catalog/",
		[],
		[],
		"" 
	],
	[
		"Контакты",
		"/contacts/",
		[],
		[],
		""
	],
];
?>
Обычно файл меню содержит один массив $aMenuLinks, каждый элемент которого описывает пункт этого меню в порядке их вывода на сайте. Каждый пункт также представляет собой массив из 5 элементов:
  • Выводимое название
  • Ссылка
  • Дополнительные ссылки при которых пункт будет активен
  • Массив переменных, которые в дальнейшем можно использовать в шаблоне при выводе
  • Условия показа пункта
Пример полностью заполненного пункта:
[
	"Партнерам",
	"/partners/", 
	[
		"/for_partners/price/",            // подсвечиваем при открытии этих адресов
		"/for_partners/conditions/"
	],
	[
		"COLOR" => "#cdcdcd",           // параметры: цвет и путь к иконке
		"ICON"   => "/icons/partner.png"
	],
	"\$GLOBALS['USER']->IsAuthorized()"   // показываем только авторизованным
],

Теперь переходим к пунктам меню второго уровня. Как мы помним, технически это означает создание файлов .left.menu.php в соответствующих папках файловой структуры сайта.

Пункты "Главная" и "Контакты", поэтому в соответствующих им директориях дополнительные меню не создаём. Хотя, если в дальнейшем нам понадобятся подпункты, то мы всегда можем это сделать.

Теперь нам нужно создать подпункты для пункта "Услуги". По нашим условиям услуги представляют собой набор статических страниц, поэтому для организации второго уровня меню нужно создать простое меню в соответствующей директории. Создаем файл /services/.left.menu.php:
<?
$aMenuLinks = [
	[
		"Услуга 1",
		"/services/serv1/",
		[],
		[],
		"" 
	],
	[
		"Услуга 2",
		"/services/serv2/", 
		[],
		[],
		"" 
	],
	[
		"Услуга 3",
		"/services/serv3/",
		[],
		[],
		"" 
	],
	[
		"Услуга 4",
		"/services/serv4/",
		[],
		[],
		""
	],
];
?>
Структура файла ничем не отличается от файла .top.menu.php описанного выше.

Теперь нам нужно вывести первый уровень разделов каталога. Задача немного сложнее, так как разделы не являются статическими страницами, а динамически выбираются из базы данных. Для создания динамических меню используются файлы .<тип меню>.menu_ext.php (помните параметр USE_EXT в настройках компонента?). Эти файлы содержат логику динамического формирования пунктов меню и добавляют их к пунктам описанным в файлах .<тип меню>.menu.php. Пункты могут формироваться квк произвольным PHP кодом, так и с помощью компонентов.

Первым делом для нашего каталога создаем файл /catalog/.left.menu.php:
<?
$aMenuLinks = [];
?>
Статических пунктов меню у нас нет и массив $aMenuLinks оставляем пустым. Но при необходимости сюда можно добавить пункты. Например, ссылку на страницу акций.

Теперь добавляем файл /catalog/.left.menu_ext.php:
<?
if( !defined("B_PROLOG_INCLUDED") || B_PROLOG_INCLUDED !== true ) {
	die();
}
global $APPLICATION;

$aMenuLinksExt = $APPLICATION->IncludeComponent(
	"bitrix:menu.sections",
	"",
	[
		"IS_SEF"           => "Y",
		"SEF_BASE_URL"     => "/catalog/",
		"SECTION_PAGE_URL" => "#SECTION_CODE_PATH#/",
		"DETAIL_PAGE_URL"  => "#SECTION_CODE_PATH#/#ELEMENT_CODE#/",
		"IBLOCK_TYPE"      => "catalog",
		"IBLOCK_ID"        => "1",
		"DEPTH_LEVEL"      => "1",
		"CACHE_TYPE"       => "A",
		"CACHE_TIME"       => "3600",
	],
	false
);
$aMenuLinks = array_merge($aMenuLinks, $aMenuLinksExt);
?>
Здесь мы используем стандартный компонент bitrix:menu.sections который предназначен для вывода разделов инфоблока сказу в формате массива полей меню. Как видим на выходе все тот же массив $aMenuLinks.

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


В качестве завершения приведем небольшой код для шаблонов меню. В стандартном $arResult все пункты меню расположены одном списком, не деревом. Определить что это подпункт можно только по параметру $arItem["DEPTH_LEVEL"]. Это не всегда удобно чтобы преобразовать список в дерево можно добавить в шаблон файл result_modifier.php со следующим кодом:
<?
function getChildren($input, &$start = 0, $level = 0){
	$children = array();

	if(!$level){
		$lastDepthLevel = 1;
		if(is_array($input)){
			foreach($input as $i => $arItem){
				if($arItem["DEPTH_LEVEL"] > $lastDepthLevel){
					if($i > 0){
						$input[$i - 1]["IS_PARENT"] = 1;
					}
				}
				$lastDepthLevel = $arItem["DEPTH_LEVEL"];
			}
		}
	}
	for($i = $start, $count = count($input); $i < $count; ++$i){
		$item = $input[$i];
		if($level > $item['DEPTH_LEVEL'] - 1){
			break;
		}
		elseif(!empty($item['IS_PARENT'])){
			++$i;
			$item['CHILDREN'] = getChildren($input, $i, $level + 1);
			--$i;
		}
		$children[] = $item;
	}

	$start = $i;
	return $children;
}

$arResult = getChildren($arResult);
?>
Таким образом мы помещаем все дочерние элементы пункта в параметр 'CHILDREN', а заодно добавляем параметр 'IS_PARENT' по которому можно определить есть ли у пункта подпункты.




Ссылки по теме:
https://dev.1c-bitrix.ru/learning/course/index.php?COURSE_ID=43&amp;LESSON_ID=3254&amp;LESSO...
https://dev.1c-bitrix.ru/learning/course/?COURSE_ID=43&amp;LESSON_ID=3498&amp;ysclid=l7034nd...

12.01.2022

Возврат к списку

Оставаясь на этом сайте Вы соглашаетесь с использованием файлов cookie, а также принимаете все пользовательские соглашения данного сайта.