Проблемы стандартных меню и наше решение
Стандартный компонент 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+ проектов по созданию кастомных решений. Гарантируем соблюдение сроков и качество. Свяжитесь с нами для оценки вашей задачи за один день — мы подготовим оптимальное решение. Получите консультацию по проекту: просто напишите нам, и мы обсудим детали.







