Проблеми стандартних меню та наше рішення
Стандартний компонент bitrix:menu генерує меню з файлів .menu.php або структури сайту. Для простих сайтів цього достатньо, але коли в каталозі тисячі товарів і потрібно керувати мегаменю з картинками, банерами, колонками — починаються гальма. Ми розробили модуль, який вирішує ці проблеми: дає гнучке керування через інтерфейс з кешуванням і повною підтримкою мегаменю. Модуль підходить для проєктів з будь-яким обсягом пунктів — перевірено на каталогах до 10 000 позицій.
Чому стандартний bitrix:menu не справляється з великими каталогами?
Одна з головних проблем — відсутність тегованого кешування. Кожен запит перебудовує дерево з файлів, а при динамічному додаванні пунктів через адміністративний інтерфейс — неможливо. Наш модуль зберігає все в базі даних і кешує дерево з тегом, інвалідація відбувається при будь-якій зміні. Це дає виграш у швидкості в 10–50 разів порівняно з розбором файлів.
Як ми реалізували масштабовану модель даних?
Ми використали ORM Бітрікс (таблиці b_vendor_menu_*) з adjacency list для зберігання ієрархії. Чому adjacency list, а не nested sets? Для меню з частими змінами структури (додавання, видалення, переміщення) adjacency list швидше на запис, а читання з кешем нівелює різницю.
Модель даних
Модуль 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
Дерево пунктів меню
Пункти зберігаються в adjacency list (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.
Порівняння зі стандартним компонентом
| Критерій | Стандартний bitrix:menu | Наш модуль |
|---|---|---|
| Керування з адмінки | Тільки через файли | Через візуальний drag-and-drop |
| Мегаменю | Вимагає ручного розмічання | Вбудовані колонки та банери |
| Кешування | Файлове, без тегів | Теговане, інвалідація за подіями |
| Видимість за групами | Тільки через умову у файлі | Інтерфейс + довільні умови |
| Термін впровадження | 2-3 дні на налаштування | 10 днів під ключ |
Процес роботи
Ми проходимо всі етапи від аналізу до впровадження:
- Аналітика — вивчаємо структуру каталогу, вимоги до видимості, інтеграції.
- Проєктування — схеми БД, прототип дерева, погодження з замовником.
- Розробка — створення ORM-модуля, компонентів, інтерфейсу адмінки.
- Тестування — перевірка на реальних даних, навантажувальне тестування.
- Впровадження — встановлення на бойовому сайті, налаштування кешування, навчання контент-менеджерів.
Що ви отримуєте в результаті?
- Модуль з ORM-таблицями, компонентом для виведення меню, адміністративним інтерфейсом.
- Документація з налаштування та використання.
- Вихідні коди в репозиторії.
- Технічна підтримка протягом 30 днів після запуску.
Терміни розробки
| Етап | Термін |
|---|---|
| ORM-таблиці, модель дерева | 1 день |
| Побудова дерева, кешування | 1 день |
| Умови видимості пунктів | 1 день |
| Мегаменю: колонки, банери | 2 дні |
| Drag-and-drop інтерфейс | 2 дні |
| Компоненти для сайту (HTML + CSS) | 2 дні |
| Тестування | 1 день |
Разом: від 10 до 15 робочих днів залежно від складності.
Оптимізація швидкості меню
Для прискорення завантаження використовуйте теговане кешування: компонент кешується на 3600 секунд з тегом `menu_{code}`. При будь-якій зміні пункту або банера кеш скидається автоматично через агент. Для великих каталогів (10 000+) рекомендуємо увімкнути композитний кеш Бітрікс та налаштувати відкладене завантаження меню через `Bitrix\Main\Page\FrameStatic`.Підхід adjacency list описаний у статті Список суміжності. Стилізація мегаменю виконується за допомогою CSS Grid.
Наш досвід розробки модулів Бітрікс — понад 7 років. Виконали 50+ проєктів зі створення кастомних рішень. Гарантуємо дотримання термінів та якість. Зв'яжіться з нами для оцінки вашого завдання за один день — ми підготуємо оптимальне рішення. Отримайте консультацію з проєкту: просто напишіть нам, і ми обговоримо деталі.







