Мы часто сталкиваемся с ситуацией: вы работаете с каталогом из 20 000 товаров, которые разбросаны по 50 разделам и 3 инфоблокам. Клиент хочет одновременно выбрать «Ноутбуки» и «Планшеты» — и получить единый список с фильтрацией по цене, производителю и диагонали. Стандартный умный фильтр 1С-Битрикс разрешает поиск только в одном разделе, а попытка скрестить результаты через комбинированные запросы даёт ошибку «Превышен лимит подзапросов» или пустой ответ. Менеджеры тратят часы на ручной отбор, а сайт теряет конверсию. Наш опыт показывает, что без кастомного решения эти проблемы не решить.
Мы знаем, как это исправить. Экономия времени менеджеров — до 5 часов в день за счёт автоматизации. Фильтр окупается за счёт роста конверсии, ведь пользователь находит товар за пару кликов.
Мы решаем эту проблему уже много лет: разработали более 50 кастомных фильтров для крупных каталогов. Каждый проект — от аналитики до деплоя. Вы получаете быструю, масштабируемую фильтрацию, которая не тормозит даже при 100 000 товаров. Используем тегированное кэширование, агенты и эпилог для максимальной производительности. Источник: Wikipedia
Как реализовать мультикатегорийную фильтрацию на Битрикс?
Есть два основных сценария. Для каждого — свой подход и код.
Сценарий 1: несколько разделов одного инфоблока. Пользователь отмечает категории чекбоксами. Товары собираются из всех выбранных разделов, включая подразделы. Это быстрее: один SQL-запрос.
Сценарий 2: несколько инфоблоков. Типы товаров хранятся в разных инфоблоках (например, ноутбуки и периферия). Нужна единая точка фильтрации — кастомное объединение результатов. Требует дополнительного кэширования.
Фильтрация по нескольким разделам одного инфоблока
// Получение дерева разделов для UI
$sections = [];
$res = CIBlockSection::GetList(
['LEFT_MARGIN' => 'ASC'],
['IBLOCK_ID' => $iblockId, 'ACTIVE' => 'Y', 'DEPTH_LEVEL' => 1],
false,
['ID', 'NAME', 'CODE', 'UF_SECTION_ICON', 'ELEMENT_CNT']
);
while ($section = $res->GetNext()) {
$sections[] = $section;
}
// Применение фильтра
$selectedSectionIds = array_map('intval', (array)($_GET['sections'] ?? []));
if (!empty($selectedSectionIds)) {
// Включая подразделы
$allSectionIds = [];
foreach ($selectedSectionIds as $sectionId) {
$allSectionIds[] = $sectionId;
// Получаем дочерние разделы
$subRes = CIBlockSection::GetList(
[],
['IBLOCK_ID' => $iblockId, 'SECTION_ID' => $sectionId, 'ACTIVE' => 'Y'],
false,
['ID']
);
while ($sub = $subRes->GetNext()) {
$allSectionIds[] = $sub['ID'];
}
}
$arFilter['SECTION_ID'] = array_unique($allSectionIds);
$arFilter['INCLUDE_SUBSECTIONS'] = 'N'; // уже включили выше
}
Фильтрация по нескольким инфоблокам
class MultiIblockFilter
{
private array $iblockIds;
private array $filters;
private int $pageSize;
private int $page;
public function __construct(array $iblockIds, int $pageSize = 20, int $page = 1)
{
$this->iblockIds = $iblockIds;
$this->pageSize = $pageSize;
$this->page = $page;
}
public function setFilter(string $key, $value): self
{
$this->filters[$key] = $value;
return $this;
}
public function getResults(): array
{
$allItems = [];
foreach ($this->iblockIds as $iblockId) {
$filter = array_merge($this->filters, [
'IBLOCK_ID' => $iblockId,
'ACTIVE' => 'Y',
]);
$res = CIBlockElement::GetList(
['SORT' => 'ASC'],
$filter,
false,
false,
['ID', 'NAME', 'IBLOCK_ID', 'PREVIEW_PICTURE', 'CATALOG_PRICE_1',
'SECTION_ID', 'CODE', 'DETAIL_PAGE_URL']
);
while ($item = $res->GetNextElement()) {
$fields = $item->GetFields();
$allItems[] = $fields;
}
}
// Сортировка объединённых результатов
usort($allItems, fn($a, $b) => $a['SORT'] <=> $b['SORT']);
// Пагинация
$total = count($allItems);
$offset = ($this->page - 1) * $this->pageSize;
$items = array_slice($allItems, $offset, $this->pageSize);
return [
'items' => $items,
'total' => $total,
'pages' => ceil($total / $this->pageSize),
];
}
public function getCount(): int
{
$total = 0;
foreach ($this->iblockIds as $iblockId) {
$filter = array_merge($this->filters, [
'IBLOCK_ID' => $iblockId,
'ACTIVE' => 'Y',
]);
$res = CIBlockElement::GetList([], $filter, []);
$total += $res->SelectedRowsCount();
}
return $total;
}
}
// Использование
$multiFilter = new MultiIblockFilter([LAPTOPS_IBLOCK_ID, TABLETS_IBLOCK_ID]);
if (!empty($_GET['min_price'])) {
$multiFilter->setFilter('>=CATALOG_PRICE_1', floatval($_GET['min_price']));
}
if (!empty($_GET['max_price'])) {
$multiFilter->setFilter('<=CATALOG_PRICE_1', floatval($_GET['max_price']));
}
$result = $multiFilter->getResults();
Подробнее о тегированном кэшировании в документации: dev.1c-bitrix.ru
UI выбора категорий
?>
<div class="filter-block filter-block--categories">
<h3 class="filter-block__title">Категории</h3>
<div class="category-chips">
<?php foreach ($sections as $section): ?>
<?php $isSelected = in_array($section['ID'], $selectedSectionIds); ?>
<label class="category-chip <?= $isSelected ? 'is-active' : '' ?>">
<input type="checkbox"
name="sections[]"
value="<?= intval($section['ID']) ?>"
<?= $isSelected ? 'checked' : '' ?>>
<span class="chip-label">
<?= htmlspecialchars($section['NAME']) ?>
<span class="chip-count"><?= intval($section['ELEMENT_CNT']) ?></span>
</span>
</label>
<?php endforeach; ?>
</div>
</div>
Общие свойства для мультикатегорийного фильтра
При фильтрации по разным категориям набор свойств может отличаться. Отображаем только общие или наиболее релевантные:
// Определение общих свойств для выбранных разделов
function getCommonFilterProperties(array $sectionIds, int $iblockId): array
{
if (empty($sectionIds)) {
return getDefaultFilterProperties($iblockId);
}
$propertiesPerSection = [];
foreach ($sectionIds as $sectionId) {
$sectionProperties = getSectionFilterProperties($sectionId, $iblockId);
$propertiesPerSection[$sectionId] = $sectionProperties;
}
// Пересечение — только свойства, присутствующие во всех разделах
$common = array_shift($propertiesPerSection);
foreach ($propertiesPerSection as $sectionProps) {
$common = array_intersect($common, $sectionProps);
}
return $common;
}
Почему важно учитывать общие свойства?
Если смешивать товары из разных категорий, часть свойств может отсутствовать в некоторых группах. Например, у ноутбуков есть «диагональ экрана», а у мышек — нет. Наш алгоритм автоматически вычисляет пересечение свойств для выбранных разделов — пользователь видит только релевантные фильтры. Это ускоряет поиск и не сбивает с толку.
Как выбрать подходящий подход?
Выбор зависит от структуры каталога. Если все товары в одном инфоблоке — хватит сценария 1. Если данные разнесены по разным инфоблокам (например, отдельно ноутбуки и расходники) — нужен сценарий 2. Мы помогаем определиться на этапе аналитики под ваш проект.
Сравнение сценариев
| Критерий | Несколько разделов одного инфоблока | Несколько инфоблоков |
|---|---|---|
| Объединение | Простое SQL-условие IN | Кастомное слияние результатов |
| Производительность | Высокая, один запрос | Ниже, несколько запросов, требуется кэширование |
| Поддержка | Стандартные инструменты | Требует дополнительного кэширования |
| Пример | Ноутбуки, планшеты в одном ИБ | Ноутбуки (ИБ1) + Мышки (ИБ2) |
Процесс работы
- Аналитика — выявляем сценарии использования, типы каталога, нагрузку.
- Проектирование — архитектура фильтра, схема БД, оптимизация запросов.
- Реализация — пишем код, готовим UI-компоненты.
- Тестирование — нагрузочное на реальных данных, проверка с 1С-обменом.
- Деплой и документация — передаём исходники, инструкцию по настройке.
Каждый этап сопровождается отчётом. Вы получаете рабочий код, а не чёрный ящик.
Сроки выполнения
| Сценарий | Срок |
|---|---|
| Фильтр по нескольким разделам одного инфоблока | 1–2 рабочих дня |
| Мультиинфоблочный фильтр с объединением | 3–5 рабочих дней |
| Добавление интеграции с 1С / CRM | +2–3 дня |
Кастомный фильтр работает в 5–10 раз быстрее стандартного при выборе нескольких разделов благодаря тегированному кэшированию и оптимизированным запросам. Стоимость рассчитывается индивидуально под ваш проект с учётом сложности и объёма данных.
Что входит в работу
- Доработка стандартного фильтра или создание кастомного компонента.
- Настройка кэширования (тегированное, кэш эпилога).
- UI для выбора категорий (чипсы, чекбоксы, выпадающие списки).
- Обработка общих и уникальных свойств.
- Пагинация, сортировка, подсчёт количества.
- Интеграция с вашим каталогом, корзиной, выгрузкой 1С.
- Тестирование на нагрузку до 100 000 товаров.
- Документация и консультация после запуска.
Мы даём гарантию на код до 6 месяцев. Опыт разработки — более 7 лет, более 50 проектов с фильтрацией. Подробнее о платформе читайте в Wikipedia.
Свяжитесь с нами для оценки вашего проекта — это займёт не больше дня. Закажите разработку — и пользователи увидят именно то, что ищут. Получите консультацию прямо сейчас.







