Розроблення модуля багаторівневого меню для 1С-Bitrix
Стандартний компонент bitrix:menu генерує меню з файлів .menu.php або зі структури сайту. Для простих сайтів цього достатньо. Проблеми починаються, коли маркетолог хоче керувати мегаменю з картинками, банерами та колонками через адміністративний інтерфейс без редагування коду, коли потрібно A/B тестувати різні структури меню або показувати різні пункти авторизованим та анонімним користувачам. Модуль багаторівневого меню надає гнучке керування через інтерфейс із кешуванням та повною підтримкою мегаменю.
Модель даних
Модуль vendor.megamenu:
-
b_vendor_menu_config— конфігурації меню: id, code, name, site_id, lang, is_active -
b_vendor_menu_item— пункти меню: id, menu_id, parent_id, sort, title, url, url_type (absolute/relative/component), target, icon_id, image_id, css_class, visibility (all/authorized/unauthorized), condition (JSON), is_active -
b_vendor_menu_column— колонки мегаменю для пункту другого рівня: id, item_id, title, sort, items (JSON — посилання без окремих записів) -
b_vendor_menu_banner— банери в мегаменю: id, item_id, image_id, url, title, sort
Дерево пунктів меню
Пункти зберігаються у списку суміжності (parent_id). Для побудови дерева використовується ітеративний обхід, щоб уникнути рекурсії у PHP:
class MenuTreeBuilder
{
public function build(int $menuId): array
{
$items = MenuItemTable::getList([
'filter' => ['MENU_ID' => $menuId, 'IS_ACTIVE' => 'Y'],
'order' => ['PARENT_ID' => 'ASC', 'SORT' => 'ASC'],
])->fetchAll();
$tree = [];
$index = [];
foreach ($items as $item) {
$item['children'] = [];
$index[$item['ID']] = &$item;
}
foreach ($index as &$item) {
if ($item['PARENT_ID']) {
$index[$item['PARENT_ID']]['children'][] = &$item;
} else {
$tree[] = &$item;
}
}
return $tree;
}
}
Дерево кешується повністю з тегом menu_{code}_{lang}. Інвалідація відбувається при будь-якій зміні пункту або банера цього меню.
Видимість пунктів
Умови видимості пункту меню:
-
visibility = 'authorized'— пункт приховано від гостей -
visibility = 'unauthorized'— пункт приховано від залогованих користувачів -
condition(JSON) — довільні умови: групи користувачів, регіон, параметри запиту
class VisibilityChecker
{
public function isVisible(array $item): bool
{
global $USER;
if ($item['VISIBILITY'] === 'authorized' && !$USER->IsAuthorized()) return false;
if ($item['VISIBILITY'] === 'unauthorized' && $USER->IsAuthorized()) return false;
$condition = $item['CONDITION'] ?? [];
if (!empty($condition['user_groups'])) {
$userGroups = $USER->GetUserGroupArray();
if (!array_intersect($condition['user_groups'], $userGroups)) return false;
}
return true;
}
}
Умови видимості застосовуються після отримання кешованого дерева — кеш зберігає повне дерево, фільтрування відбувається в пам'яті.
Мегаменю з колонками та банерами
Для пунктів верхнього рівня можна налаштувати мегаменю-розкривний список з колонками:
[Каталог]
├─ Колонка 1: Електроніка
│ ├─ Смартфони
│ ├─ Ноутбуки
│ └─ Планшети
├─ Колонка 2: Побутова техніка
│ ├─ Холодильники
│ └─ Пральні машини
└─ Банер: [Акції тижня → /sale/]
Колонки зберігаються у b_vendor_menu_column, банери — у b_vendor_menu_banner. Компонент генерує HTML-структуру мегаменю, стилізація — через CSS Grid.
Інтерфейс Drag-and-drop
Адміністративний інтерфейс побудований на Sortable.js (або аналогічному). Пункти перетягуються у потрібну позицію та до потрібного батька. При перетягуванні AJAX-запит оновлює sort та parent_id однією транзакцією.
Активний пункт меню
Активний пункт меню визначається за поточною URL:
// Точне збігання URL або збігання початку шляху
function isActive(array $item, string $currentUrl): bool
{
if ($item['URL'] === $currentUrl) return true;
if ($item['URL_TYPE'] === 'section' && str_starts_with($currentUrl, $item['URL'])) return true;
return false;
}
Активний пункт та всі його предки отримують CSS-клас active.
Терміни розроблення
| Етап | Тривалість |
|---|---|
| ORM-таблиці, модель дерева | 1 день |
| Побудова дерева, кешування | 1 день |
| Умови видимості пунктів | 1 день |
| Мегаменю: колонки, банери | 2 дні |
| Інтерфейс Drag-and-drop | 2 дні |
| Компоненти для сайту (HTML + CSS) | 2 дні |
| Тестування | 1 день |
Разом: 10 робочих днів. Для мобільного меню (offcanvas, гамбургер) — додатково 1 день.







