Brand Filtering for 1C-Bitrix
The brand filter is one of the key features in most online stores. Technically this is filtering by a list property or by a link to an element in a dedicated brands infoblock. However, a brand filter demands special UX: logos next to the brand name, alphabetical grouping for large brand sets, highlighting popular brands, and fast brand search.
Two Ways to Store Brands
Method 1: List property (PROPERTY_TYPE = L). Values in b_iblock_property_enum. Simple, but limited: no way to store a logo image or additional attributes.
Method 2: Element link to an infoblock (PROPERTY_TYPE = E). A dedicated "Brands" infoblock with its own properties: logo, country, description, SEO data. More flexible, used in large catalogs.
Retrieving Brands with Product Counts
// For a list property
function getBrandsWithCount(int $iblockId, int $sectionId): array
{
$brands = [];
// Get all values of the BRAND property with product counts
$rs = \CIBlockElement::GetList(
['PROPERTY_BRAND_VALUE' => 'ASC'],
[
'IBLOCK_ID' => $iblockId,
'ACTIVE' => 'Y',
'SECTION_ID' => $sectionId,
'INCLUDE_SUBSECTIONS' => 'Y',
],
['PROPERTY_BRAND'],
false,
['ID', 'PROPERTY_BRAND_VALUE', 'PROPERTY_BRAND_ENUM_ID']
);
while ($el = $rs->Fetch()) {
$enumId = $el['PROPERTY_BRAND_ENUM_ID'];
$name = $el['PROPERTY_BRAND_VALUE'];
if ($enumId) {
$brands[$enumId]['name'] = $name;
$brands[$enumId]['id'] = $enumId;
$brands[$enumId]['count'] = ($brands[$enumId]['count'] ?? 0) + 1;
}
}
return array_values($brands);
}
For an element link — join with the brands infoblock to retrieve the logo:
function getBrandsWithLogos(int $iblockId, int $brandIblockId): array
{
// First, get unique brand IDs present in the catalog
$brandIds = [];
$rs = \CIBlockElement::GetList(
[],
['IBLOCK_ID' => $iblockId, 'ACTIVE' => 'Y'],
['PROPERTY_BRAND'],
false,
['PROPERTY_BRAND_VALUE']
);
while ($el = $rs->Fetch()) {
if ($el['PROPERTY_BRAND_VALUE']) {
$brandIds[] = (int)$el['PROPERTY_BRAND_VALUE'];
}
}
$brandIds = array_unique($brandIds);
// Then retrieve brand data including logos
$brands = [];
$brandRs = \CIBlockElement::GetList(
['SORT' => 'ASC'],
['IBLOCK_ID' => $brandIblockId, 'ID' => $brandIds, 'ACTIVE' => 'Y'],
false,
false,
['ID', 'NAME', 'PREVIEW_PICTURE', 'DETAIL_PAGE_URL']
);
while ($brand = $brandRs->Fetch()) {
$logo = $brand['PREVIEW_PICTURE']
? \CFile::GetPath($brand['PREVIEW_PICTURE'])
: null;
$brands[] = [
'id' => $brand['ID'],
'name' => $brand['NAME'],
'logo' => $logo,
'url' => $brand['DETAIL_PAGE_URL'],
];
}
return $brands;
}
Brand Filter Template
<!-- Brand filter with logos -->
<div class="brand-filter">
<div class="brand-filter__search">
<input type="text" id="brand-search" placeholder="Search brand..." />
</div>
<div class="brand-filter__popular">
<span class="brand-filter__label">Popular</span>
<div class="brand-filter__logos" id="popular-brands">
<!-- Top-10 brand logos -->
</div>
</div>
<div class="brand-filter__list" id="brand-list">
<div class="brand-filter__group" v-for="letter in alphabet">
<div class="brand-filter__letter">{{ letter }}</div>
<label v-for="brand in brandsByLetter[letter]" :key="brand.id">
<input type="checkbox" :value="brand.id" v-model="selectedBrands" />
<img v-if="brand.logo" :src="brand.logo" :alt="brand.name" loading="lazy" />
<span>{{ brand.name }}</span>
<span class="brand-filter__count">({{ brand.count }})</span>
</label>
</div>
</div>
</div>
Alphabetical Grouping and Search
// Group brands by first letter
function groupBrandsByLetter(brands) {
const groups = {}
const cyrillic = 'АБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯ'
brands.forEach(brand => {
const firstChar = brand.name[0].toUpperCase()
const letter = cyrillic.includes(firstChar) || /[A-Z]/.test(firstChar)
? firstChar
: '#'
if (!groups[letter]) groups[letter] = []
groups[letter].push(brand)
})
return groups
}
// Search with debounce
const searchInput = document.getElementById('brand-search')
let searchTimer = null
searchInput.addEventListener('input', () => {
clearTimeout(searchTimer)
searchTimer = setTimeout(() => {
const query = searchInput.value.toLowerCase()
filterBrandsList(query)
}, 200)
})
function filterBrandsList(query) {
document.querySelectorAll('.brand-item').forEach(item => {
const name = item.dataset.brandName.toLowerCase()
item.style.display = name.includes(query) ? '' : 'none'
})
// Hide empty letter groups
document.querySelectorAll('.brand-filter__group').forEach(group => {
const visibleItems = group.querySelectorAll('.brand-item:not([style*="none"])')
group.style.display = visibleItems.length ? '' : 'none'
})
}
Brand Page in the Catalog
Clicking a brand in the filter leads to a filtered catalog or a dedicated brand page. For SEO — a dedicated page is preferable:
// Routing: /catalog/brand/apple/ → filter by brand
$brandCode = $arUrlTemplates['brand']; // from human-readable URL
$brandElement = \CIBlockElement::GetList(
[],
['IBLOCK_ID' => BRANDS_IBLOCK_ID, 'CODE' => $brandCode],
false, false, ['ID', 'NAME']
)->Fetch();
if ($brandElement) {
$arFilter['PROPERTY_BRAND'] = $brandElement['ID'];
}
Popular Brands
Top brands by product count or by views — displayed as logos in a dedicated block:
// Sort by product count — determined when building the brand list
usort($brands, fn($a, $b) => $b['count'] - $a['count']);
$popularBrands = array_slice($brands, 0, 10);
Timeline
Brand filter (list property) with checkboxes and AJAX — 1–2 business days. Full-featured brand filter with logos, search, alphabetical grouping, and brand pages — 3–4 business days.







