1С-Битрикс: создаём свой компонент с классом и кэшированием
Компонент — основной способ вывести данные на страницу в Битриксе. Стандартных компонентов много, но рано или поздно понадобится свой: стандартный делает лишнее, не умеет нужного, а копировать и править его неудобно — при обновлении правки потеряются.
Свои компоненты кладём в /local/components/, а не в /bitrix/components/: папку /bitrix перезаписывают обновления.
Будем делать компонент devlib:news.latest — список последних новостей из инфоблока.
/local/components/devlib/news.latest/
├── .description.php — название и место в визуальном редакторе
├── .parameters.php — настройки компонента
├── class.php — логика
├── lang/ru/... — языковые файлы (по желанию)
└── templates/
└── .default/
├── template.php — вёрстка
└── style.css — подключится автоматически
Описать компонент
Сделайте так, чтобы компонент появился в визуальном редакторе в отдельной группе «DevLib».
Показать решение Скрыть решение
<?php
if (!defined('B_PROLOG_INCLUDED') || B_PROLOG_INCLUDED !== true) {
die();
}
$arComponentDescription = [
'NAME' => 'Последние новости',
'DESCRIPTION' => 'Выводит N последних активных элементов инфоблока',
'PATH' => [
'ID' => 'devlib',
'NAME' => 'DevLib',
],
'CACHE_PATH' => 'Y',
];
Строка с B_PROLOG_INCLUDED есть в каждом файле компонента: она запрещает открыть файл напрямую по URL.
Добавить параметры
Компонент должен принимать ID инфоблока, количество элементов и время кэширования.
Показать решение Скрыть решение
<?php
if (!defined('B_PROLOG_INCLUDED') || B_PROLOG_INCLUDED !== true) {
die();
}
$arComponentParameters = [
'PARAMETERS' => [
'IBLOCK_ID' => [
'PARENT' => 'BASE',
'NAME' => 'ID инфоблока',
'TYPE' => 'STRING',
'DEFAULT' => '',
],
'COUNT' => [
'PARENT' => 'BASE',
'NAME' => 'Количество элементов',
'TYPE' => 'STRING',
'DEFAULT' => '5',
],
'CACHE_TIME' => ['DEFAULT' => 3600],
],
];
CACHE_TIME — системный параметр: Битрикс сам добавит его в группу настроек кэша.
Написать логику в class.php
Получите элементы, сохраните в $arResult и подключите шаблон. Результат должен кэшироваться.
Показать решение Скрыть решение
<?php
if (!defined('B_PROLOG_INCLUDED') || B_PROLOG_INCLUDED !== true) {
die();
}
use Bitrix\Main\Application;
use Bitrix\Main\Loader;
use Bitrix\Iblock\ElementTable;
class DevlibNewsLatestComponent extends CBitrixComponent
{
/** Проверка и нормализация параметров — до кэша */
public function onPrepareComponentParams($arParams): array
{
$arParams['IBLOCK_ID'] = (int) ($arParams['IBLOCK_ID'] ?? 0);
$arParams['COUNT'] = max(1, (int) ($arParams['COUNT'] ?? 5));
$arParams['CACHE_TIME'] = (int) ($arParams['CACHE_TIME'] ?? 3600);
return $arParams;
}
public function executeComponent(): void
{
if ($this->arParams['IBLOCK_ID'] <= 0) {
ShowError('Не указан ID инфоблока');
return;
}
// startResultCache: true — кэша нет, надо собрать данные; false — данные из кэша
if ($this->startResultCache()) {
if (!Loader::includeModule('iblock')) {
$this->abortResultCache();
ShowError('Модуль «Информационные блоки» не установлен');
return;
}
$this->arResult['ITEMS'] = $this->getItems();
if (!$this->arResult['ITEMS']) {
$this->abortResultCache(); // пустой результат не кэшируем
}
// Тегированный кэш: модуль iblock сбрасывает тег iblock_id_N
// при добавлении, изменении и удалении элементов
Application::getInstance()->getTaggedCache()
->registerTag('iblock_id_' . $this->arParams['IBLOCK_ID']);
$this->includeComponentTemplate();
}
}
private function getItems(): array
{
$rows = ElementTable::getList([
'select' => ['ID', 'NAME', 'CODE', 'PREVIEW_TEXT', 'ACTIVE_FROM'],
'filter' => [
'=IBLOCK_ID' => $this->arParams['IBLOCK_ID'],
'=ACTIVE' => 'Y',
],
'order' => ['ACTIVE_FROM' => 'DESC', 'ID' => 'DESC'],
'limit' => $this->arParams['COUNT'],
])->fetchAll();
foreach ($rows as &$row) {
$row['DATE'] = $row['ACTIVE_FROM']
? $row['ACTIVE_FROM']->format('d.m.Y')
: '';
}
return $rows;
}
}
Как работает кэш компонента:
- ключ кэша строится из параметров компонента и шаблона, поэтому два вызова с разным
IBLOCK_IDкэшируются отдельно; - всё, что выполняется внутри
if ($this->startResultCache()), при попадании в кэш не выполняется — включая шаблон. Вывод шаблона кэшируется целиком; - запросы через ORM сами теги не регистрируют, поэтому тег
iblock_id_Nдобавляем вручную. Если на сайте включено управляемое (тегированное) кэширование, кэш компонента сбросится при изменении элементов инфоблока. Иначе — по истеченииCACHE_TIME.
Сверстать шаблон
Компонент кладёт новости в $arResult['ITEMS'], у каждой есть NAME, CODE, PREVIEW_TEXT и DATE (может быть пустой). Сверстайте шаблон .default: для каждой новости — дата (если есть), заголовок-ссылка на /news/{CODE}/ и анонс.
Данные приходят из базы, их заполняют редакторы — выводите их так, чтобы через название новости нельзя было внедрить HTML или скрипт.
Показать решение Скрыть решение
<?php
if (!defined('B_PROLOG_INCLUDED') || B_PROLOG_INCLUDED !== true) {
die();
}
/** @var array $arResult */
?>
<div class="news-latest">
<?php foreach ($arResult['ITEMS'] as $item): ?>
<article class="news-latest__item">
<?php if ($item['DATE']): ?>
<time class="news-latest__date"><?= $item['DATE'] ?></time>
<?php endif; ?>
<h3 class="news-latest__title">
<a href="/news/<?= htmlspecialcharsbx($item['CODE']) ?>/">
<?= htmlspecialcharsbx($item['NAME']) ?>
</a>
</h3>
<div class="news-latest__text"><?= $item['PREVIEW_TEXT'] ?></div>
</article>
<?php endforeach; ?>
</div>
htmlspecialcharsbx — функция Битрикса для экранирования, используйте её для всего, что ввёл пользователь. PREVIEW_TEXT здесь выводится без экранирования, потому что редактор в админке сохраняет его как HTML. Если в инфоблоке тип текста «text», экранируйте и его.
Файл style.css из папки шаблона Битрикс подключит сам.
Вызвать компонент на странице
Выведите на главной странице сайта (/index.php) три последние новости из инфоблока с ID 1. Шаблон — по умолчанию, кэш — на час, с учётом общих настроек кэширования сайта.
Показать решение Скрыть решение
<?php
require $_SERVER['DOCUMENT_ROOT'] . '/bitrix/header.php';
$APPLICATION->SetTitle('Главная');
?>
<?php $APPLICATION->IncludeComponent(
'devlib:news.latest',
'', // пустая строка = шаблон .default
[
'IBLOCK_ID' => 1,
'COUNT' => 3,
'CACHE_TYPE' => 'A', // A — авто (по настройкам сайта), Y — всегда, N — никогда
'CACHE_TIME' => 3600,
]
); ?>
<?php require $_SERVER['DOCUMENT_ROOT'] . '/bitrix/footer.php'; ?>
Добавить второй шаблон без копирования логики
Нужно вывести те же новости в подвале сайта компактным списком.
Показать решение Скрыть решение
Логика остаётся в class.php, добавляем только новую папку шаблона:
<?php
if (!defined('B_PROLOG_INCLUDED') || B_PROLOG_INCLUDED !== true) {
die();
}
?>
<ul class="news-compact">
<?php foreach ($arResult['ITEMS'] as $item): ?>
<li><a href="/news/<?= htmlspecialcharsbx($item['CODE']) ?>/"><?= htmlspecialcharsbx($item['NAME']) ?></a></li>
<?php endforeach; ?>
</ul>
$APPLICATION->IncludeComponent('devlib:news.latest', 'compact', ['IBLOCK_ID' => 1, 'COUNT' => 5]);
Шаблон можно переопределить и на уровне шаблона сайта: файл /local/templates/<шаблон_сайта>/components/devlib/news.latest/compact/template.php имеет приоритет над шаблоном внутри компонента.
Итог
- Свои компоненты — в
/local/components/<пространство>/<имя>/. - Логика — в
class.php(наследникCBitrixComponent), вёрстка — в шаблонах. - Всё, что должно кэшироваться, — внутри
startResultCache(). Пустой или ошибочный результат —abortResultCache(). - Вывод данных — через
htmlspecialcharsbx.