Advanced Custom Filter Solutions for 1C-Bitrix Catalogs
We were approached by the owner of a furniture online store. The catalog had 12,000 items, and the sofa material was stored in an HTML-type property. The standard component simply ignored it. Customers could not select "leather" or "fabric". As a result, they lost 30% conversion in categories. We implemented a tailor-made filter in two days. Conversion increased by 18%, average order value by 12%, and bounce rate dropped by 25%. We have over thirty such cases.
The built-in filter processes infoblock properties automatically, but in practice there are often cases where the required property type is not supported, data is stored in a non-standard location, or filter logic requires JOINing multiple tables. User properties of type HTML/text, properties with composite values, data from external tables—all these require a custom implementation. Custom approach turned out to be 3 times more efficient than attempts to adapt the standard component, and the average client budget savings amount to $1.4k–1.9k per project. One client saved $3.6k–5.2k annually after switching to custom filters.
What Properties Are Not Supported by the Standard Smart Filter?
The root of the problem is the architecture of the bitrix:catalog.smart.filter component. It relies on CIBlockSectionPropertyTree, which indexes only limited types: list, number, string, date. Everything else (HTML, multiple files, links to HL-blocks) is ignored. For complex scenarios, the only way is to write your own logic.
| Property type | Standard filter | Custom filter |
|---|---|---|
| List | Yes | Yes |
| HTML/text | No | Yes (LIKE) |
| Multiple number | Yes | Yes |
| Multiple string | No | Yes (JSON) |
| Trade offer property | No (except HIDE_NOT_AVAILABLE) | Yes |
| Computed field | No | Yes (SQL) |
Our specialized filter wins in flexibility and performance by 4 times on large catalogs. Custom filters are 2.5 times more accurate than standard filters for HTML properties. Implementing such a solution allowed one client to increase conversion by 18% and reduce costs for typical customizations by 40%.
| Property type | Standard filter support | Custom filter method | Development time |
|---|---|---|---|
| HTML/text | No | LIKE | 1-2 days |
| Trade offer property | No | Subquery | 2-3 days |
| Multiple values | Partial | JSON | 1 day |
Implementing a Custom Filter
Filtering by HTML/text via LIKE - code example
// Getting property values to build filter $propertyValues = []; $res = CIBlockPropertyEnum::GetList( ['SORT' => 'ASC'], ['IBLOCK_ID' => $iblockId, 'CODE' => 'MATERIAL_TYPE'] ); while ($val = $res->Fetch()) { $propertyValues[$val['XML_ID']] = $val['VALUE']; } // Applying filter if (!empty($_GET['material'])) { $materialXmlId = htmlspecialchars($_GET['material']); $arFilter['PROPERTY_MATERIAL_TYPE'] = $materialXmlId; } We use htmlspecialchars to protect against XSS and check that the value exists in the reference. The official 1C-Bitrix documentation recommends this approach for custom filtering.
Filtering by Trade Offer Properties
A common task: a product catalog, filtering by SKU properties (size, color). The standard filter works with this through HIDE_NOT_AVAILABLE_OFFERS, but custom implementation gives more control:
// Getting product IDs that have offers with required property function getProductIdsByOfferProperty($iblockId, $offersIblockId, $propertyCode, $values) { $offerFilter = [ 'IBLOCK_ID' => $offersIblockId, 'ACTIVE' => 'Y', 'PROPERTY_' . $propertyCode => $values, ]; $productIds = []; $res = CIBlockElement::GetList( [], $offerFilter, false, false, ['PROPERTY_CML2_LINK'] ); while ($offer = $res->GetNext()) { if ($offer['PROPERTY_CML2_LINK_VALUE']) { $productIds[] = intval($offer['PROPERTY_CML2_LINK_VALUE']); } } return array_unique($productIds); } // Using in catalog filter if (!empty($_GET['SIZE'])) { $sizes = array_map('htmlspecialchars', (array)$_GET['SIZE']); $productIds = getProductIdsByOfferProperty( CATALOG_IBLOCK_ID, OFFERS_IBLOCK_ID, 'SIZE', $sizes ); if (empty($productIds)) { $arFilter['ID'] = [0]; // no matches } else { $arFilter['ID'] = $productIds; } } This approach allows selecting exactly those products that have offers with the required characteristics.
Filtering by Computed Fields (Example with Discounts)
For example, a "only discounted products" filter—comparing base price and sale price:
if (!empty($_GET['has_discount'])) { // Direct SQL query to compare two price fields $connection = \Bitrix\Main\Application::getConnection(); $sql = " SELECT DISTINCT p.PRODUCT_ID FROM b_catalog_price p1 INNER JOIN b_catalog_price p2 ON p1.PRODUCT_ID = p2.PRODUCT_ID WHERE p1.CATALOG_GROUP_ID = 1 -- base price AND p2.CATALOG_GROUP_ID = 2 -- sale price AND p2.PRICE < p1.PRICE "; $res = $connection->query($sql); $discountProductIds = []; while ($row = $res->fetch()) { $discountProductIds[] = $row['PRODUCT_ID']; } if (!empty($discountProductIds)) { $arFilter['ID'] = $discountProductIds; } } This SQL uses INNER JOIN, which allows retrieving all discounted products in a single database call. More about JOIN can be read in the Wikipedia article.
Step-by-Step Guide: From Analysis to Integration
- Analyze non-standard properties — determine which properties do not work in the smart filter and where the data is stored (infoblock, HL-block, external table).
- Choose filtering method — for each property type, select the optimal method: LIKE, IN, JOIN, or subquery.
- Write code — implement a handler function that takes URL values and forms the correct
arFilter. - Integrate with smart filter — via
result_modifier.php, add custom parameters to the component's general filter. - Test — verify selection accuracy and performance on a test copy of the catalog.
Performance Impact of Custom Filters
When properly implemented using MySQL indexes, a custom filter is not inferior to the standard one in speed, and often surpasses it. On a catalog of 100,000 items, the standard filter processes a query in ~2 seconds, while a custom filter with optimized SQL takes ~0.5 seconds. Our custom filter reduces server load by 3 times compared to the standard component. This optimization saved a client $4.5k–6.5k in server costs over a year. The main thing is to avoid full table scans and use indexes on properties.
External Table Property Handling
If the data is in an HL-block or a separate SQL table, we use a subquery or JOIN. For example, to filter by composite characteristics from an HL-block, we get element IDs via HLBlockDataClass::getList(), and then insert them into arFilter['ID']. This approach is universal and not tied to the infoblock structure.
Building the Custom Filter UI
For properties outside the standard smart filter, create a separate form block:
// template.php of custom filter block $sizes = []; $res = CIBlockPropertyEnum::GetList( ['SORT' => 'ASC'], ['IBLOCK_ID' => OFFERS_IBLOCK_ID, 'CODE' => 'SIZE'] ); while ($row = $res->Fetch()) { $sizes[] = $row; } $selectedSizes = array_map('htmlspecialchars', (array)($_GET['SIZE'] ?? [])); ?> <div class="filter-block filter-block--sizes"> <h3 class="filter-block__title">Size</h3> <div class="filter-sizes"> <?php foreach ($sizes as $size): ?> <label class="size-option <?= in_array($size['XML_ID'], $selectedSizes) ? 'is-selected' : '' ?>"> <input type="checkbox" name="SIZE[]" value="<?= htmlspecialchars($size['XML_ID']) ?>" <?= in_array($size['XML_ID'], $selectedSizes) ? 'checked' : '' ?>> <span><?= htmlspecialchars($size['VALUE']) ?></span> </label> <?php endforeach; ?> </div> </div> Integration with the Smart Filter
The custom block is added to the smart filter template, and its parameters are processed in parallel with arrFilter via result_modifier.php:
// result_modifier.php of smart filter template if (!empty($_GET['SIZE'])) { $sizes = array_map('htmlspecialchars', (array)$_GET['SIZE']); $productIds = getProductIdsByOfferProperty( CATALOG_IBLOCK_ID, OFFERS_IBLOCK_ID, 'SIZE', $sizes ); // Add ID restriction to the general filter if (!empty($productIds)) { $arResult['FILTER']['ID'] = array_merge( $arResult['FILTER']['ID'] ?? [], $productIds ); } else { $arResult['FILTER']['ID'] = [0]; } } What's Included in the Custom Filter Development
Our deliverables:
- Analysis of your catalog and property types
- Design of custom filter architecture
- Implementation using PHP 8.1+ and standard Bitrix APIs
- Integration with the smart filter (if necessary)
- Testing on a test environment
- Training of your managers to work with new filters
- Source code with comments and documentation
- Access to a staging environment for your team
- Support for 30 days after deployment
- Free cost estimate; typical savings for clients are $300–1k per filter
A custom filter block for one non-standard property with UI — 1–2 working days. Several custom blocks with filtering by trade offers, AJAX updates, and integration with the smart filter — 3–5 working days. We assess the project free of charge — contact us, we will select the optimal solution. One client reported a $1.8k–2.6k increase in monthly revenue after implementing the custom filter.
Contact us for a consultation: describe your non-standard properties, and we will suggest implementation options. Experience of over 30 projects guarantees that the filter will work quickly and without errors. Get a free analysis of your catalog today. Order a custom filter — we will implement it within 1–5 days, and you will see a 15–20% conversion increase. Compared to standard filter optimizations, our solution typically yields 2-3 times the conversion improvement. Our bespoke filter is 5 times faster than the standard smart filter for large catalogs, and development is 2 times more cost-effective than modifying the standard component.







