Разработка фильтрации по размерам 1С-Битрикс
Фильтр по размерам в каталогах одежды, обуви и мебели работает иначе, чем большинство других фильтров. Размер — как правило, свойство торгового предложения, а не родительского товара. Пользователь хочет видеть только товары, у которых его размер в наличии, — не просто товары, у которых этот размер когда-либо существовал. Разница принципиальная и требует фильтрации с проверкой остатков.
Архитектура данных для размеров
Товар (инфоблок каталога)
└── Торговые предложения (инфоблок торговых предложений)
├── PROPERTY_SIZE = "S" CATALOG_QUANTITY = 3
├── PROPERTY_SIZE = "M" CATALOG_QUANTITY = 0
└── PROPERTY_SIZE = "L" CATALOG_QUANTITY = 7
Фильтр «размер M» при включённом «Только в наличии» не должен показывать этот товар — предложение M в наличии отсутствует.
Хранение размеров
Текстовое свойство (S, M, L, XL) — простые каталоги. XML_ID = S, VALUE = S или локализованное Маленький.
Числовое свойство (36, 37, 38... для обуви) — удобно для диапазонной фильтрации.
Сложные размеры (32/34, EU 42 / US 9) — хранятся как перечисление с кодом и несколькими отображениями.
Получение размеров с учётом остатков
function getAvailableSizes(int $offersIblockId): array
{
$sizes = [];
// Получаем только предложения с ненулевым остатком
$res = CIBlockElement::GetList(
[],
[
'IBLOCK_ID' => $offersIblockId,
'ACTIVE' => 'Y',
'>CATALOG_QUANTITY' => 0,
],
['PROPERTY_SIZE'],
false,
['PROPERTY_SIZE']
);
while ($item = $res->Fetch()) {
$sizeId = $item['PROPERTY_SIZE_ENUM_ID'];
$sizeValue = $item['PROPERTY_SIZE_VALUE'];
if ($sizeId && !isset($sizes[$sizeId])) {
$sizes[$sizeId] = [
'id' => $sizeId,
'xmlId' => $item['PROPERTY_SIZE_ENUM_XML_ID'],
'value' => $sizeValue,
'sort' => 0, // заполним ниже
];
}
}
// Сортировка из настроек свойства
if (!empty($sizes)) {
$enumRes = CIBlockPropertyEnum::GetList(
['SORT' => 'ASC'],
['IBLOCK_ID' => $offersIblockId, 'CODE' => 'SIZE']
);
$sortMap = [];
while ($enum = $enumRes->Fetch()) {
$sortMap[$enum['ID']] = intval($enum['SORT']);
}
foreach ($sizes as &$size) {
$size['sort'] = $sortMap[$size['id']] ?? 999;
}
usort($sizes, fn($a, $b) => $a['sort'] <=> $b['sort']);
}
return array_values($sizes);
}
Фильтрация каталога по размеру с остатком
function getProductIdsBySize(
int $catalogIblockId,
int $offersIblockId,
array $sizeXmlIds,
bool $onlyInStock = true
): array {
if (empty($sizeXmlIds)) return [];
$offerFilter = [
'IBLOCK_ID' => $offersIblockId,
'ACTIVE' => 'Y',
'PROPERTY_SIZE' => $sizeXmlIds,
];
if ($onlyInStock) {
$offerFilter['>CATALOG_QUANTITY'] = 0;
}
$productIds = [];
$res = CIBlockElement::GetList(
[],
$offerFilter,
false,
false,
['PROPERTY_CML2_LINK']
);
while ($row = $res->GetNext()) {
if ($pid = intval($row['PROPERTY_CML2_LINK_VALUE'])) {
$productIds[$pid] = true;
}
}
return array_keys($productIds);
}
UI: сетка размеров
$availableSizes = getAvailableSizes(OFFERS_IBLOCK_ID);
$selectedSizes = array_map('htmlspecialchars', (array)($_GET['SIZE'] ?? []));
?>
<div class="filter-block filter-block--sizes">
<h3 class="filter-block__title">Размер</h3>
<div class="size-grid">
<?php foreach ($availableSizes as $size): ?>
<?php $isSelected = in_array($size['xmlId'], $selectedSizes); ?>
<label class="size-option <?= $isSelected ? 'is-selected' : '' ?>">
<input type="checkbox"
name="SIZE[]"
value="<?= htmlspecialchars($size['xmlId']) ?>"
<?= $isSelected ? 'checked' : '' ?>>
<span class="size-label"><?= htmlspecialchars($size['value']) ?></span>
</label>
<?php endforeach; ?>
</div>
</div>
CSS сетки размеров
.size-grid {
display: flex;
flex-wrap: wrap;
gap: 6px;
}
.size-option input[type="checkbox"] {
display: none;
}
.size-label {
display: flex;
align-items: center;
justify-content: center;
min-width: 40px;
height: 36px;
padding: 0 8px;
border: 1px solid #ddd;
border-radius: 4px;
font-size: 13px;
cursor: pointer;
transition: background 0.15s, border-color 0.15s, color 0.15s;
user-select: none;
}
.size-option.is-selected .size-label {
background: #1a1a1a;
border-color: #1a1a1a;
color: #fff;
}
.size-option:hover:not(.is-selected) .size-label {
border-color: #999;
}
Кеширование доступных размеров
$cacheId = 'available_sizes_' . OFFERS_IBLOCK_ID;
$cache = \Bitrix\Main\Data\Cache::createInstance();
if ($cache->initCache(600, $cacheId, '/catalog/filter/')) {
$availableSizes = $cache->getVars();
} else {
$availableSizes = getAvailableSizes(OFFERS_IBLOCK_ID);
$cache->startDataCache();
$cache->endDataCache($availableSizes);
}
Инвалидация при изменении остатков через тег кеша или агент каждые 10 минут.
Сроки выполнения
Фильтр по размерам через свойство перечисления без проверки остатков — 4–8 часов. Полная реализация с проверкой наличия по SKU, кешированием, кастомным UI и сортировкой размеров — 2–3 рабочих дня.







