Разберём полную реализацию шаблона brand_products для компонента catalog.section с выводом товаров конкретного бренда, пагинацией и сеткой 6×5 (6 карточек в ряд, 5 рядов = 30 товаров на странице).
1. Создание структуры шаблона
Создайте папку шаблона по пути (рекомендуется /local/):
/local/templates/ваш_шаблон/components/bitrix/catalog.section/brand_products/
├── template.php
├── style.css
├── script.js
├── .parameters.php
└── component_epilog.php (опционально)
2. Вызов компонента на странице (с фильтрацией по бренду)
Разместите вызов компонента на странице бренда (например, /brands/nike/). В параметр FILTER_NAME передаётся имя переменной, содержащей массив фильтра:
$GLOBALS['arrFilterBrand'] = array(
'PROPERTY_BRAND_VALUE' => 'Nike' // или ID бренда: '=PROPERTY_BRAND' => 12
);
$APPLICATION->IncludeComponent(
'bitrix:catalog.section',
'brand_products',
array(
'IBLOCK_TYPE' => 'catalog',
'IBLOCK_ID' => IB_CATALOG_ID,
'SECTION_ID' => '', // если не нужна привязка к разделу
'SECTION_CODE' => '',
'SECTION_USER_FIELDS' => array(''),
'ELEMENT_SORT_FIELD' => 'sort',
'ELEMENT_SORT_ORDER' => 'asc',
'FILTER_NAME' => 'arrFilterBrand', // имя переменной с фильтром
'PAGE_ELEMENT_COUNT' => '30', // 6×5 = 30 товаров
'LINE_ELEMENT_COUNT' => '6', // 6 карточек в ряд
'PROPERTY_CODE' => array('BRAND', 'ARTICLE', 'COLOR'),
'PAGER_TEMPLATE' => '.default',
'DISPLAY_TOP_PAGER' => 'N',
'DISPLAY_BOTTOM_PAGER' => 'Y',
'PAGER_TITLE' => 'Товары бренда',
'PAGER_SHOW_ALWAYS' => 'N',
'PAGER_DESC_NUMBERING' => 'N',
'PAGER_DESC_NUMBERING_CACHE_TIME' => '36000',
'PAGER_SHOW_ALL' => 'N',
'PAGER_BASE_LINK' => '',
'SET_TITLE' => 'Y',
'SET_STATUS_404' => 'Y',
'SHOW_404' => 'Y',
'MESSAGE_404' => '',
'CACHE_TYPE' => 'A',
'CACHE_TIME' => '36000000',
'CACHE_FILTER' => 'Y',
'CACHE_GROUPS' => 'Y',
),
$component
);
Ключевые параметры для вашей задачи:
FILTER_NAME— имя PHP-переменной, через которую передаётся фильтр по бренду.PAGE_ELEMENT_COUNT = 30— всего 30 товаров на странице.LINE_ELEMENT_COUNT = 6— 6 товаров в одном ряду.
3. Файл template.php — полный код шаблона
Используем ваш фрагмент с настройками пагинации и добавляем сетку 6×5 со своими стилями карточек:
<?php if (!defined('B_PROLOG_INCLUDED') || B_PROLOG_INCLUDED !== true) die();
/** @var CBitrixComponentTemplate $this */
/** @var array $arParams */
/** @var array $arResult */
/** @var string $templateName */
/** @var string $templateFile */
/** @var string $templateFolder */
/** @var array $templateData */
$this->setFrameMode(true);
// --- Ваш код из фрагмента (настройка пагинации) ---
if (!empty($arResult['NAV_RESULT'])) {
$navParams = array(
'NavPageCount' => $arResult['NAV_RESULT']->NavPageCount,
'NavPageNomer' => $arResult['NAV_RESULT']->NavPageNomer,
'NavNum' => $arResult['NAV_RESULT']->NavNum
);
} else {
$navParams = array(
'NavPageCount' => 1,
'NavPageNomer' => 1,
'NavNum' => $this->randString()
);
}
$showTopPager = false;
$showBottomPager = false;
$showLazyLoad = false;
if ($arParams['PAGE_ELEMENT_COUNT'] > 0 && $navParams['NavPageCount'] > 1) {
$showTopPager = $arParams['DISPLAY_TOP_PAGER'];
$showBottomPager = $arParams['DISPLAY_BOTTOM_PAGER'];
$showLazyLoad = $arParams['LAZY_LOAD'] === 'Y' && $navParams['NavPageNomer'] != $navParams['NavPageCount'];
}
$templateLibrary = array('popup', 'ajax', 'fx');
$currencyList = '';
if (!empty($arResult['CURRENCIES'])) {
$templateLibrary[] = 'currency';
$currencyList = CUtil::PhpToJSObject($arResult['CURRENCIES'], false, true, true);
}
$templateData = array(
'TEMPLATE_LIBRARY' => $templateLibrary,
'CURRENCIES' => $currencyList,
'USE_PAGINATION_CONTAINER' => $showTopPager || $showBottomPager,
);
unset($currencyList, $templateLibrary);
$elementEdit = CIBlock::GetArrayByID($arParams['IBLOCK_ID'], 'ELEMENT_EDIT');
$elementDelete = CIBlock::GetArrayByID($arParams['IBLOCK_ID'], 'ELEMENT_DELETE');
$elementDeleteParams = array('CONFIRM' => GetMessage('CT_BCS_TPL_ELEMENT_DELETE_CONFIRM'));
$positionClassMap = array(
'left' => 'product-item-label-left',
'center' => 'product-item-label-center',
'right' => 'product-item-label-right',
'bottom' => 'product-item-label-bottom',
'middle' => 'product-item-label-middle',
'top' => 'product-item-label-top',
);
// --- Конец вашего фрагмента ---
// Проверка на 404 при превышении номера страницы
if ($navParams['NavPageNomer'] > $navParams['NavPageCount']) {
\CHTTP::SetStatus('404 Not Found');
require $_SERVER['DOCUMENT_ROOT'] . '/404.php';
exit;
}
?>
<?php if (!empty($arResult['ITEMS'])): ?>
<div class="brand-products-wrapper">
<?php if ($showTopPager): ?>
<div class="brand-pager brand-pager--top" data-pagination-num="=$navParams['NavNum']?>">
<?=$arResult['NAV_STRING']?>
</div>
<?php endif; ?>
<div class="brand-products-grid">
<div class="brand-products-grid__row">
<?php
$lineCount = (int)$arParams['LINE_ELEMENT_COUNT'] ?: 6; // 6 в ряд
$counter = 0;
foreach ($arResult['ITEMS'] as $item):
$counter++;
// Подготовка данных карточки
$productId = $item['ID'];
$productName = $item['NAME'];
$productPreview = $item['PREVIEW_PICTURE']
? CFile::GetPath($item['PREVIEW_PICTURE'])
: $templateFolder . '/images/no-photo.png';
$productDetailUrl = $item['DETAIL_PAGE_URL'];
$productPrice = $item['MIN_PRICE']['DISPLAY_VALUE'] ?? '';
$productOldPrice = $item['MIN_PRICE']['DISCOUNT_VALUE'] ?? '';
$productCurrency = $item['MIN_PRICE']['CURRENCY'] ?? 'RUB';
// Свои свойства (например, бренд уже в фильтре, можно вывести)
$brandValue = $item['PROPERTIES']['BRAND']['VALUE'] ?? '';
?>
<div class="brand-product-card">
<a href="=$productDetailUrl?>" class="brand-product-card__link">
<div class="brand-product-card__image-wrapper">
<img
class="brand-product-card__image"
src="=$productPreview?>"
alt="=htmlspecialcharsbx($productName)?>"
loading="lazy"
/>
<?php if ($productOldPrice > $productPrice): ?>
<span class="brand-product-card__badge brand-product-card__badge--sale">%</span>
<?php endif; ?>
</div>
<div class="brand-product-card__info">
<?php if ($brandValue): ?>
<span class="brand-product-card__brand">=htmlspecialcharsbx($brandValue)?></span>
<?php endif; ?>
<h3 class="brand-product-card__title">=htmlspecialcharsbx($productName)?></h3>
<div class="brand-product-card__price">
<?php if ($productOldPrice > $productPrice): ?>
<span class="brand-product-card__price-old">=$productOldPrice?></span>
<?php endif; ?>
<span class="brand-product-card__price-current">
<?=$productPrice?> <?=$productCurrency?>
</span>
</div>
</div>
</a>
<button
class="brand-product-card__add-to-cart"
data-product-id="=$productId?>"
type="button"
>
В корзину
</button>
</div>
<?php if ($counter % $lineCount === 0 && $counter < count($arResult['ITEMS'])): ?>
</div><!-- /.row -->
<div class="brand-products-grid__row">
<?php endif; ?>
<?php endforeach; ?>
</div><!-- /.row -->
</div><!-- /.brand-products-grid -->
<?php if ($showBottomPager): ?>
<div class="brand-pager brand-pager--bottom" data-pagination-num="=$navParams['NavNum']?>">
<?=$arResult['NAV_STRING']?>
</div>
<?php endif; ?>
</div>
<?php else: ?>
<div class="brand-products-empty">
<p>Товары данного бренда не найдены.</p>
</div>
<?php endif; ?>
4. Файл style.css — свои стили на 6 карточек в ряд
/* --- Контейнер --- */
.brand-products-wrapper {
max-width: 1200px;
margin: 0 auto;
padding: 20px 0;
}
/* --- Сетка: Flexbox — 6 колонок --- */
.brand-products-grid {
width: 100%;
}
.brand-products-grid__row {
display: flex;
flex-wrap: wrap;
margin: 0 -10px 20px;
}
/* Карточка = 1/6 ширины ряда (≈16.666%) */
.brand-product-card {
flex: 0 0 calc(100% / 6);
max-width: calc(100% / 6);
padding: 0 10px;
margin-bottom: 28px;
box-sizing: border-box;
}
/* --- Сама карточка товара --- */
.brand-product-card__link {
display: block;
text-decoration: none;
color: inherit;
border: 1px solid #e8e8e8;
border-radius: 8px;
overflow: hidden;
background: #fff;
transition: box-shadow 0.2s ease, transform 0.2s ease;
}
.brand-product-card__link:hover {
box-shadow: 0 4px 20px rgba(0,0,0,0.1);
transform: translateY(-2px);
}
/* --- Изображение --- */
.brand-product-card__image-wrapper {
position: relative;
width: 100%;
padding-top: 100%; /* квадрат */
overflow: hidden;
background: #f7f7f7;
}
.brand-product-card__image {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
object-fit: cover;
transition: transform 0.3s ease;
}
.brand-product-card__link:hover .brand-product-card__image {
transform: scale(1.05);
}
/* --- Бейдж скидки --- */
.brand-product-card__badge {
position: absolute;
top: 8px;
right: 8px;
background: #ff4a4a;
color: #fff;
font-size: 12px;
font-weight: 700;
padding: 3px 8px;
border-radius: 4px;
z-index: 2;
}
/* --- Информация --- */
.brand-product-card__info {
padding: 12px 14px 14px;
}
.brand-product-card__brand {
font-size: 11px;
text-transform: uppercase;
color: #999;
letter-spacing: 0.5px;
display: block;
margin-bottom: 4px;
}
.brand-product-card__title {
font-size: 14px;
line-height: 1.3;
font-weight: 500;
margin: 0 0 10px;
color: #222;
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
}
/* --- Цена --- */
.brand-product-card__price {
display: flex;
align-items: center;
gap: 8px;
flex-wrap: wrap;
}
.brand-product-card__price-current {
font-size: 16px;
font-weight: 700;
color: #d32f2f;
}
.brand-product-card__price-old {
font-size: 13px;
color: #aaa;
text-decoration: line-through;
}
/* --- Кнопка "В корзину" --- */
.brand-product-card__add-to-cart {
display: block;
width: 100%;
padding: 10px 0;
border: none;
border-radius: 0 0 8px 8px;
background: #2b7a2b;
color: #fff;
font-size: 13px;
font-weight: 600;
text-transform: uppercase;
cursor: pointer;
transition: background 0.2s ease;
letter-spacing: 0.5px;
}
.brand-product-card__add-to-cart:hover {
background: #1e5e1e;
}
/* --- Пагинация --- */
.brand-pager {
text-align: center;
padding: 20px 0;
clear: both;
}
.brand-pager .modern-page-navigation {
display: inline-flex;
gap: 4px;
}
/* --- Состояние "нет товаров" --- */
.brand-products-empty {
text-align: center;
padding: 60px 20px;
color: #888;
font-size: 16px;
}
/* --- Адаптивность --- */
@media (max-width: 992px) {
.brand-product-card {
flex: 0 0 25%; /* 4 колонки */
max-width: 25%;
}
}
@media (max-width: 768px) {
.brand-product-card {
flex: 0 0 33.3333%; /* 3 колонки */
max-width: 33.3333%;
}
}
@media (max-width: 480px) {
.brand-product-card {
flex: 0 0 50%; /* 2 колонки */
max-width: 50%;
}
}
5. Файл .parameters.php (расширенные настройки шаблона)
Опционально можно добавить свои параметры в визуальный редактор:
<?php if (!defined('B_PROLOG_INCLUDED') || B_PROLOG_INCLUDED !== true) die();
use Bitrix\Main\Localization\Loc;
Loc::loadMessages(__FILE__);
$arTemplateParameters = array(
'LINE_ELEMENT_COUNT' => array(
'PARENT' => 'VISUAL',
'NAME' => 'Количество элементов в строке',
'TYPE' => 'STRING',
'DEFAULT' => '6',
),
'SHOW_BRAND_LABEL' => array(
'PARENT' => 'VISUAL',
'NAME' => 'Показывать название бренда на карточке',
'TYPE' => 'CHECKBOX',
'DEFAULT' => 'Y',
),
);
6. Проверка обработки 404-страницы
В template.php добавлен блок проверки, который при превышении номера страницы отдаёт 404 — это защищает от индексации пустых страниц пагинации (рекомендация из документации Битрикс):
if ($navParams['NavPageNomer'] > $navParams['NavPageCount']) {
\CHTTP::SetStatus('404 Not Found');
require $_SERVER['DOCUMENT_ROOT'] . '/404.php';
exit;
}
Итоговая схема работы
- Фильтрация по бренду — через параметр
FILTER_NAMEи глобальную переменную$arrFilterBrandс условием по свойствуBRAND. - Пагинация — 30 товаров на странице (код, который вы предоставили, полностью сохранён и расширен).
- Сетка 6×5 — Flexbox с
flex: 0 0 calc(100% / 6)(6 колонок) иPAGE_ELEMENT_COUNT = 30(5 рядов). - Свои стили — карточка с изображением, названием бренда, ценой, скидочным бейджем и кнопкой «В корзину».
- Адаптивность — на планшетах 3–4 колонки, на телефонах 2 колонки.
- Защита от 404 — при несуществующей странице пагинации отдаётся 404.
Разместите шаблон по пути /local/templates/ваш_шаблон/components/bitrix/catalog.section/brand_products/ и выберите его в настройках компонента catalog.section — параметр Шаблон → brand_products.