Свой компонент Битрикс: как сделать правильно с первого раза
Блок «похожие товары» можно собрать четырьмя способами, и три из них вы будете поддерживать годами. Разбираем цену каждого варианта и каркас компонента, который не придётся переписывать после первого релиза.

Задача бытовая: вывести на карточке товара блок «похожие предложения» — четыре позиции из того же раздела. Очевидное решение — скопировать bitrix:news.list в /local/components, выпилить лишнее и дописать свою выборку. Через неделю выясняется, что в копии больше тысячи строк чужого кода, кеш не учитывает ID раздела, и у всех посетителей висят одни и те же четыре товара. Ниже — чем отличаются четыре способа получить свой вывод и какой каркас не придётся переделывать. Примеры рассчитаны на «1С-Битрикс: Управление сайтом» 23.x–24.x, ядро D7, PHP 8.1.
Что решается до первой строки кода
Три решения, которые потом меняются дороже всего.
Место. Только /local/components/вендор/имя.компонента/. Папка /bitrix/components/ перезаписывается при обновлении ядра: ваш код оттуда однажды просто исчезнет. Вендор — любой свой префикс латиницей (имя студии, домен клиента), он же станет первой частью вызова: mycompany:catalog.similar.
Простой или комплексный. Простой компонент отдаёт один блок на существующей странице. Комплексный держит несколько страниц под одним URL и сам разбирает адрес — это нужно, если у вас список и детальная страница с ЧПУ. Для блока «похожие» комплексный не нужен никогда.
Кто формирует данные. Вся выборка живёт в class.php, шаблон только выводит. Запрос в базу из template.php работает ровно до того момента, когда кто-то включит кеширование.
Четыре способа и цена каждого
Свой шаблон стандартного компонента
Копируете templates/.default стандартного компонента в /local/templates/имя_шаблона/components/bitrix/news.list/similar/ и переписываете вывод. PHP-кода ноль, обновления ядра ничего не ломают. Платите тем, что ограничены содержимым $arResult, который собрал за вас стандартный компонент: нужного поля там может просто не быть. Частичный выход — result_modifier.php, он выполняется внутри кешируемой области и может дополнить массив.
Копия стандартного компонента в /local
Самый популярный и самый дорогой вариант. Скопировав bitrix:catalog.section, вы получаете на поддержку несколько тысяч строк, которые писали не вы, и перестаёте получать исправления и новые возможности ядра для этого компонента. Оправдано, когда нужно поменять одну деталь в сложной логике витрины — работу с ценами, скидками, предложениями, — и повторять её с нуля дороже.
Свой простой компонент
Пять файлов, свой $arResult, понятный кеш. Писать параметры и кеширование придётся руками, зато через год код читается целиком за десять минут. Для блоков, подборок, форм и интеграций — вариант по умолчанию.
Комплексный компонент
Отдельная история с маршрутизацией, страницами внутри одного компонента, обработкой 404 и каноническими адресами. Берите, только если под компонентом действительно несколько URL. Маршрутизация в комплексных компонентах — тема отдельного разбора, здесь её не трогаем.
| Подход | Когда подходит | Чем платите | Обновления ядра |
|---|---|---|---|
| Свой шаблон стандартного | Данные устраивают, меняется только HTML | Зависите от чужого arResult | Безопасно |
| Копия в /local | Нужна правка внутри сложной витринной логики | Тысячи строк чужого кода на поддержке | Правки ядра к вам не приедут |
| Свой простой компонент | Своя выборка, один вид вывода | Кеш и параметры пишете сами | Не затрагивается |
| Комплексный компонент | Несколько страниц под одним URL | Маршрутизация, 404, канонические адреса | Не затрагивается |
Когда что выбирать
- Меняется только разметка, данные те же — шаблон, без PHP.
- Не хватает одного-двух полей к готовому списку — шаблон плюс
result_modifier.php. - Своя логика выборки и свой набор данных — свой простой компонент.
- Нужно вмешаться в расчёт цен, скидок или торговых предложений внутри стандартной витрины — копия стандартного компонента, с записью в документации проекта, что вы теперь владелец этого кода.
- Под блоком появляются свои URL — комплексный компонент, а не три простых, прикрученных к трём физическим страницам.
Каркас, который не придётся переделывать
Минимальный набор файлов внутри папки компонента:
class.php— класс-наследникCBitrixComponent, вся логика;.description.php— массив$arComponentDescription: имя, раздел в визуальном редакторе,CACHE_PATH => 'Y'для вкладки настроек кеша;.parameters.php— массив$arComponentParametersс вашими параметрами иCACHE_TIME;templates/.default/template.php— вывод по умолчанию;lang/ru/— подписи, если компонент пойдёт на несколько проектов.
Скелет класса. Два места, которые чаще всего делают неправильно, — нормализация параметров и ключ кеша.
<?php
// /local/components/mycompany/catalog.similar/class.php
// 1С-Битрикс 23.x–24.x, ядро D7, PHP 8.1
if (!defined('B_PROLOG_INCLUDED') || B_PROLOG_INCLUDED !== true) {
die();
}
use Bitrix\Main\Loader;
class CatalogSimilarComponent extends CBitrixComponent
{
// Приводим параметры к типам один раз — дальше по коду им можно верить
public function onPrepareComponentParams($arParams)
{
$arParams['IBLOCK_ID'] = (int)($arParams['IBLOCK_ID'] ?? 0);
$arParams['SECTION_ID'] = (int)($arParams['SECTION_ID'] ?? 0);
$arParams['COUNT'] = (int)($arParams['COUNT'] ?? 4);
if ($arParams['COUNT'] < 1 || $arParams['COUNT'] > 50) {
$arParams['COUNT'] = 4;
}
// Без этой строки startResultCache() получит false и кеш не включится
$arParams['CACHE_TIME'] = isset($arParams['CACHE_TIME'])
? (int)$arParams['CACHE_TIME']
: 3600;
return $arParams;
}
public function executeComponent()
{
global $USER;
if (!Loader::includeModule('iblock')) {
ShowError('Модуль «Информационные блоки» не установлен');
return;
}
// Группы влияют на права чтения элементов: без них один посетитель
// получит кеш, собранный для другого
$userGroups = is_object($USER) ? $USER->GetUserGroupArray() : [2];
$cacheKey = [
$this->arParams['IBLOCK_ID'],
$this->arParams['SECTION_ID'],
$this->arParams['COUNT'],
implode(',', $userGroups),
LANGUAGE_ID,
];
if ($this->startResultCache(false, $cacheKey)) {
$this->arResult['ITEMS'] = $this->loadItems();
if (!$this->arResult['ITEMS']) {
// Иначе пустой результат осядет в кеше на час
$this->abortResultCache();
return;
}
// Только перечисленные ключи доедут до component_epilog.php
$this->setResultCacheKeys(['ITEMS']);
$this->includeComponentTemplate();
}
}
}
Метод loadItems() — ваша выборка. Для инфоблоков это по-прежнему CIBlockElement::GetList(): он умеет свойства и учитывает права доступа. Таблицы D7 вроде \Bitrix\Iblock\ElementTable удобнее синтаксисом, но прав на элементы не применяют, так что для публичной части это осознанный выбор, а не замена по умолчанию.
В шаблоне — только вывод и экранирование. Данные из инфоблока приходят такими, какими их ввёл контент-менеджер:
<?php if (!defined('B_PROLOG_INCLUDED') || B_PROLOG_INCLUDED !== true) die(); ?>
<div class="similar">
<?php foreach ($arResult['ITEMS'] as $item): ?>
<a class="similar__item" href="<?= htmlspecialcharsbx($item['DETAIL_PAGE_URL']) ?>">
<?= htmlspecialcharsbx($item['NAME']) ?>
</a>
<?php endforeach; ?>
</div>
Файлы style.css и script.js рядом с шаблоном ядро подключает само — отдельных вызовов для них писать не нужно.
Кеш: что попадёт внутрь и кому это достанется
Компонент без кеша на карточке товара — это лишние запросы на каждый хит. Компонент с кешем без правильного ключа — это чужие данные в чужом браузере. Всё, что влияет на результат, обязано быть в ключе: ID инфоблока и раздела, количество, группы пользователя, язык, валюта, параметры фильтра из URL.
Главный вопрос к новому компоненту не «работает ли он», а «что именно попадёт в кеш и чего из этого нельзя отдавать другому пользователю».
Если нужно сбрасывать кеш не по таймеру, а по изменению контента, подключайте тегированный кеш:
use Bitrix\Main\Application;
$taggedCache = Application::getInstance()->getTaggedCache();
if ($this->startResultCache(false, $cacheKey)) {
$taggedCache->startTagCache($this->getCachePath());
// Имя тега для инфоблоков формирует модуль iblock — сверьте в документации
$taggedCache->registerTag('iblock_id_' . $this->arParams['IBLOCK_ID']);
$this->arResult['ITEMS'] = $this->loadItems();
$taggedCache->endTagCache();
$this->includeComponentTemplate();
}
Работает это только при включённом управляемом кеше (константа BX_COMP_MANAGED_CACHE в dbconn.php); состояние настройки проверьте в документации своей версии ядра, прежде чем закладываться на автосброс.
Тегированный кеш — механизм, в котором каждый сохранённый фрагмент помечается метками (тегами) вроде «инфоблок №5». Когда контент с такой меткой меняется, ядро само удаляет все кеши с этим тегом, не дожидаясь истечения времени жизни.
Что ломается в проде
- Правки не видны. Отдаётся старый кеш. Сбросьте его из публичной части в режиме правки или через очистку файлов кеша в административной панели; в крайнем случае чистится содержимое
/bitrix/cache/и/bitrix/managed_cache/. - Заголовок страницы меняется только на первом хите. Вызов
$APPLICATION->SetTitle()оказался внутри кешируемой области. Переносите его вcomponent_epilog.php, а нужные данные протаскивайте черезsetResultCacheKeys()— в эпилоге доступны только эти ключи. - В блоке чужие данные. Имя пользователя, корзина, персональная цена попали в общий кеш. Персональное выносится в эпилог, в отдельный ajax-запрос или в динамическую область композитного сайта.
- Ядро игнорирует class.php. В файле нет класса, унаследованного от
CBitrixComponent, и выполняется старыйcomponent.php— либо не выполняется ничего. - Компонента нет в визуальном редакторе. Почти всегда дело в
.description.php: не заполненPATHили файл падает с ошибкой, которую вы не видите из-за выключенного вывода ошибок. - Блок пуст у всех после единственного сбоя. Забыли
abortResultCache()на ветке «данных нет» — и пустота зафиксировалась на весь срок жизни кеша.
Что делать дальше
Первое: прогоните компонент под включённым кешем и проверьте, что в /bitrix/cache/ появляются разные файлы для разных значений параметров — это самая дешёвая проверка ключа. Второе: посмотрите число запросов к базе на странице в отладочной панели до и после включения кеша; если разница меньше, чем вы ожидали, кеш не включился или ключ ломается на каждом хите. Третье: решите, как компонент будет вести себя в композитном режиме, пока он не уехал на боевой сервер. И почитайте в документации разделы про result_modifier.php, component_epilog.php и параметры шаблона — именно там живут границы между кешируемым и персональным.
Инфографика для маркетплейса: Три главных преимущества товара короткими фразами, выноски в безопасной зоне превью, точный размер площадки.
Спросить за 60 ₽Источники: 1С-Битрикс: документация по классу CBitrixComponent, Учебный курс «Разработчик Bitrix Framework», разделы о компонентах, шаблонах и кешировании
Частые вопросы
Чем простой компонент отличается от комплексного и как не ошибиться с выбором?
Можно ли унаследовать стандартный компонент вместо того, чтобы копировать его целиком?
У меня Битрикс 20.x, а не 24.x — код подойдёт?
Что произойдёт в проде, если забыть abortResultCache на ветке без данных?
Как показывать персональные данные, если компонент закеширован?
Почему нельзя просто поправить файл в /bitrix/components, если правка маленькая?
Материал носит информационный характер и подготовлен редакцией «Агентуры». Он не является офертой, рекламой или индивидуальной консультацией. Упомянутые продукты, компании и торговые знаки принадлежат их правообладателям. Перед принятием решений, влекущих юридические или финансовые последствия, обратитесь к профильному специалисту.