Перейти к содержимому

Вывод разделов - дерево, подразделы, адреса

Выводим дерево разделов на странице каталога и разбираемся, откуда берутся адреса самих разделов.

Решение

Выводим разделы текущего уровня:

$APPLICATION->IncludeComponent('bitrix:catalog.section.list', '', [
'IBLOCK_TYPE' => 'catalog',
'IBLOCK_ID' => 5,
'SECTION_ID' => $sectionId,
'DEPTH_LEVEL' => 2, // глубина дерева, а не номер уровня
'COUNT_ELEMENTS' => 'Y', // считать товары в разделах
'VIEW_MODE' => 'LINE',
]);

Компонент показывает подразделы того раздела, который ему указан. Без параметра глубины он ограничивается одним уровнем вложенности, и это самая частая причина жалобы «выводятся только верхние категории».

Получаем разделы из кода одним запросом:

$res = CIBlockSection::GetList(
['LEFT_MARGIN' => 'ASC'], // порядок обхода дерева
['IBLOCK_ID' => 5, 'ACTIVE' => 'Y'],
true, // с подсчётом элементов
['ID', 'NAME', 'DEPTH_LEVEL', 'SECTION_PAGE_URL', 'ELEMENT_CNT']
);
while ($section = $res->GetNext()) {
printf("%s%-30s товаров=%s\n",
str_repeat(' ', $section['DEPTH_LEVEL'] - 1),
$section['NAME'], $section['ELEMENT_CNT']);
}

Сортировка по левой границе выдаёт дерево в порядке обхода: сначала родитель, следом его дети, затем следующий родитель. Уровень вложенности приходит отдельным полем выборки, и рекурсия по разделам здесь не нужна вовсе.

Выбираем разделы конкретной ветки:

$filter = [
'IBLOCK_ID' => 5,
'ACTIVE' => 'Y',
'>LEFT_MARGIN' => $parent['LEFT_MARGIN'], // всё, что лежит внутри ветки
'<RIGHT_MARGIN' => $parent['RIGHT_MARGIN'],
];

Левая и правая границы раздела описывают его поддерево целиком. Такой фильтр достаёт всех потомков на любую глубину одним запросом, тогда как фильтр по родительскому разделу вернёт только непосредственных детей.

Дерево разделов удобно держать в памяти одним массивом. Одна выборка с сортировкой по левой границе даёт готовый порядок обхода, и остаётся только проставить отступы по уровню вложенности. Рекурсивные запросы по одному разделу за раз на дереве в сотню узлов обходятся в сотню обращений к базе.

Настраиваем адрес раздела:

$iblock = new CIBlock();
$iblock->Update(5, [
// путь раздела подставляется целиком, включая всех его родителей
'SECTION_PAGE_URL' => '/catalog/#SECTION_CODE_PATH#/',
'DETAIL_PAGE_URL' => '/catalog/#SECTION_CODE_PATH#/#ELEMENT_CODE#/',
]);

Шаблон адреса раздела живёт в настройках инфоблока, а не в параметрах компонента. Физических папок под разделы при этом не создаётся: одна страница каталога обслуживает их все, разбирая адрес по правилу обработки.

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

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

Типичные проблемы

Выводятся только разделы верхнего уровня.

Не задана глубина вывода. Компонент по умолчанию показывает только один верхний уровень всего дерева разделов.

В разделе с подразделами пустая страница.

Стоит только компонент списка товаров. Подразделы выводит отдельный компонент.

Разделы выводятся в случайном порядке.

Сортировка идёт не по левой границе дерева. Порядок обхода задаётся именно этим полем.

Ссылка на раздел ведёт в 404.

Шаблон адреса раздела не совпадает с правилом обработки адресов. Компонент собирает ссылку по одному шаблону, а разбирается адрес по другому.

Страница дерева разделов открывается медленно.

Включён подсчёт элементов в разделах. Он делает отдельный запрос на каждый узел дерева.

Частые вопросы

Как вывести подразделы вместе с товарами?

Двумя компонентами на одной странице: сначала список разделов, потом список элементов. Один компонент оба списка не отдаёт.

Как получить все разделы, к которым привязан товар?

Через таблицу связи элементов и разделов. Поле раздела в самом элементе хранит только основную привязку.

Можно ли ограничить число выводимых категорий?

Да, параметром количества у компонента или ограничением выборки в своём коде. Порядок при этом задают сортировкой, иначе отсечение непредсказуемо.

Почему у раздела нет своей страницы на диске?

Разделы не создают файлов: все адреса разбирает одна страница каталога по правилу обработки адресов.

Смежное

Первоисточник