Меню с деревом разделов каталога в модальном окне с использованием Bootstrap для 1С-Битрикс


Техническая реализация

1. Компонент каталога

result_modifier.php

<?
use Helper\CatalogSectionHelper;

if (!defined("B_PROLOG_INCLUDED") || B_PROLOG_INCLUDED !== true) die();

// Получение иерархической структуры разделов
$arResult = CatalogSectionHelper::getTree(
    IBLOCK_CATALOG_ID, // ID инфоблока каталога
    false               // Флаг отключения загрузки элементов
);

// Формирование левой панели меню
if (!empty($arResult)) {
    foreach ($arResult as $section) {
        if ($section['DEPTH_LEVEL'] == 1) {
            $arResult['LEFT'][] = [
                'NAME' => $section['NAME'],
                'CODE' => $section['CODE'],
            ];
        }
    }
}

2. Шаблон отображения (template.php)

<? if (!empty($arResult)): ?>
<!-- Триггер модального окна -->
<button type="button" 
        data-bs-toggle="modal" 
        data-bs-target="#catalogMenuModal" 
        class="btn btn-primary btn-secondary">
    Открыть каталог
</button>

<!-- Контейнер меню -->
<div class="catalog-menu">
    <div class="container">
        <div class="menu-list">
            <!-- Левая панель (родительские разделы) -->
            <ul class="main-category-list">
                <? foreach ($arResult['LEFT'] as $k => $sectionLeft): ?>
                <li class="category-item<?= $k === 0 ? ' active' : ''?>" 
                    onclick="openCategory(event, '<?= $sectionLeft['CODE']?>')">
                    <span class="category-link">
                        <?= $sectionLeft['NAME'] ?>
                    </span>
                </li>
                <? endforeach ?>
            </ul>

            <!-- Правая панель (дочерние разделы) -->
            <div class="subcategory-list">
                <? foreach ($arResult as $i => $section): ?>
                <div class="tab-list<?= $i === 0 ? ' active' : ''?>" 
                     id="<?= $section['CODE']?>">
                    <a class="category-title" 
                       href="<?= $section['DETAIL_PAGE_URL']?>">
                        <?= $section['NAME'] ?>
                    </a>
                    <? if(!empty($section['SUB_SECTIONS'])): 
                        renderSubSection($section['SUB_SECTIONS']);
                    endif ?>
                </div>
                <? endforeach ?>
            </div>
        </div>
    </div>
</div>
<? endif ?>

<? 
// Рекурсивная функция отрисовки подразделов
function renderSubSection(array $subSections, int $level = 1): void {
    foreach ($subSections as $subSection):
?>
    <div class="lev-<?= $level ?>">
        <a class="sub-title-link" 
           href="<?= $subSection['DETAIL_PAGE_URL']?>">
            <?= $subSection['NAME'] ?>
        </a>
        <? if (!empty($subSection['SUB_SECTIONS'])): 
            renderSubSection($subSection['SUB_SECTIONS'], $level + 1);
        endif ?>
    </div>
<? endforeach } ?>

Вспомогательные классы

CatalogSectionHelper

<?php
namespace Helper;

use Bitrix\Main\{Loader, ORM\Query\Result};
use Bitrix\Iblock\SectionTable;

class CatalogSectionHelper 
{
    const DEFAULT_SELECT = [
        'ID',
        'CODE',
        'NAME',
        'IBLOCK_ID',
        'IBLOCK_SECTION_ID',
        'DEPTH_LEVEL',
        'PICTURE',
        'SECTION_PAGE_URL_RAW' => 'IBLOCK.SECTION_PAGE_URL'
    ];

    /**
     * Получение древовидной структуры разделов
     * @param int|null $iblockId - ID инфоблока
     * @param bool $needElements - Флаг загрузки элементов
     * @param int $parentSectionId - ID родительского раздела
     * @return array
     */
    public static function getTree(
        ?int $iblockId, 
        bool $needElements, 
        int $parentSectionId = 0
    ): array {
        if (!Loader::includeModule('iblock')) {
            throw new \Exception('Модуль инфоблоков не установлен');
        }

        $result = [];
        $sections = self::getSections($iblockId, $parentSectionId);

        foreach ($sections as $section) {
            $section['DETAIL_PAGE_URL'] = UrlHelper::getSectionUrl($section);
            $section['SUB_SECTIONS'] = self::getTree(
                $iblockId, 
                $needElements, 
                $section['ID']
            );
            $result[$section['ID']] = $section;
        }

        return $result;
    }

    private static function getSections(
        ?int $iblockId, 
        int $parentId
    ): Result {
        return SectionTable::getList([
            'select' => self::DEFAULT_SELECT,
            'filter' => [
                'IBLOCK_ID' => $iblockId,
                'IBLOCK_SECTION_ID' => $parentId,
                'ACTIVE' => 'Y'
            ],
            'order' => ['LEFT_MARGIN' => 'ASC'],
            'cache' => ['ttl' => 3600]
        ]);
    }
}

UrlHelper

<?php
namespace Helper;

class UrlHelper 
{
    /**
     * Генерация ЧПУ для элементов
     */
    public static function getDetailPageUrl(array $element): string 
    {
        return \CIBlock::ReplaceDetailUrl(
            $element['DETAIL_PAGE_URL'], 
            $element, 
            false, 
            'E'
        );
    }

    /**
     * Генерация ЧПУ для разделов
     */
    public static function getSectionUrl(array $section): string 
    {
        return \CIBlock::ReplaceDetailUrl(
            $section['SECTION_PAGE_URL_RAW'], 
            $section, 
            true, 
            'S'
        );
    }
}

Интеграция с Bootstrap 5

Основные функции:

  1. Модальное окно: Использует стандартный Bootstrap Modal

    <div class="modal fade" id="catalogMenuModal">
     <div class="modal-dialog modal-fullscreen">
       <div class="modal-content">
         <!-- Вставка содержимого компонента -->
       </div>
     </div>
    </div>
  2. Навигация между табами:\ Функция openCategory активирует соответствующие разделы:

    function openCategory(event, sectionCode) {
     document.querySelectorAll('.tab-list').forEach(tab => {
       tab.classList.remove('active');
     });
     document.getElementById(sectionCode).classList.add('active');
    }

Особенности реализации

  1. Производительность:

    • Кеширование запросов к БД (TTL 3600 сек)
    • Рекурсивная загрузка разделов только при необходимости
  2. Адаптивность:

    • Поддержка мобильных устройств через modal-fullscreen
    • Прогрессивное улучшение для JS-отключенных сценариев
  3. Расширяемость:

    • Возможность подключения элементов инфоблока через параметр $needElements
    • Гибкая система классов для кастомизации стилей

Для полной интеграции подключите Bootstrap 5 и добавьте недостающие стили в соответствии с дизайн-системой проекта.