Компонент изнутри - вызов, кэш, шаблон, эпилог
Разбираем компонент по шагам: что происходит от строки вызова до готовой разметки, где именно лежит кэш и почему правки в одном файле шаблона работают, а в другом молчат.
Механика
Вызов компонента запускает цепочку из четырёх понятных и предсказуемых этапов. Платформа находит код компонента, готовит параметры, выполняет логику и подключает шаблон с готовыми данными.
Параметры нормализует отдельный метод подготовки, работающий раньше всякой логики самого компонента. Именно там приводят типы, подставляют значения по умолчанию и чинят чужие вызовы с пустыми строками вместо чисел.
Массив параметров приходит в шаблон компонента уже экранированным самой платформой. Исходные значения лежат в тех же ключах с волной впереди, и для сравнения с данными базы берут именно их.
Логика компонента складывает подготовленные данные в общий массив результата. Шаблон получает и результат, и параметры, и объект самого компонента вместе с путём своей папки.
Шаблон ищется в пяти местах по порядку, и выигрывает первый найденный. Сначала смотрят папку своего шаблона сайта, затем общую папку, затем то же самое в ядре и лишь в конце папку самого компонента.
Кэш результата хранит и готовую разметку, и ограниченный набор ключей результата. При попадании в кэш шаблон не выполняется вовсе: платформа отдаёт сохранённую разметку целиком.
Правка результата в шаблоне выполняется до вывода и попадает в кэш. Поэтому изменение этого файла не видно, пока кэш компонента не сброшен.
Эпилог шаблона выполняется после вывода и на каждом запросе мимо кэша. В него выносят то, что кэшировать нельзя: динамический заголовок страницы, счётчики, персональные подписи.
Состав данных, доступных эпилогу, ограничен теми ключами, которые объявлены для кэша. Забытый ключ превращается в пустоту именно в эпилоге, хотя в шаблоне он был.
Режим действий компонента работает в обход его основного метода выполнения. При вызове действия выполняется подготовка параметров и само действие, а основная логика не запускается вообще.
Шаги
- Найти класс компонента и убедиться, что вызов идёт из каталога проекта.
- Посмотреть подготовку параметров и привести типы всех значений в одном месте.
- Проверить, какой именно шаблон выигрывает поиск по пяти каталогам самой платформы.
- Понять, попадает ли запрос в кэш результата и какие ключи в нём сохранены.
- Разнести правки: результат правят до шаблона, а некэшируемое выносят в эпилог.
Код
Готовим параметры до логики:
// class.php своего компонентаpublic function onPrepareComponentParams($arParams): array{ $arParams['COUNT'] = (int) ($arParams['COUNT'] ?? 20) ?: 20; $arParams['IBLOCK_ID'] = (int) $arParams['IBLOCK_ID']; $arParams['CACHE_TIME'] = isset($arParams['CACHE_TIME']) ? (int) $arParams['CACHE_TIME'] : 3600; return $arParams; // сюда приходят значения ровно из вызова компонента // метод работает и при вызове действия, поэтому проверки лучше держать здесь}Приведение типов в одном месте избавляет логику от проверок. Компонент, вызванный из чужого шаблона со строкой вместо числа, продолжает работать предсказуемо.
Выполняем логику с кэшем результата:
public function executeComponent(): void{ if ($this->startResultCache(false, [$this->arParams['IBLOCK_ID'], $GLOBALS['USER']->GetGroups()])) { $this->arResult['ITEMS'] = $this->loadItems(); if (!$this->arResult['ITEMS']) { $this->abortResultCache(); } // пустое не кэшируем $this->setResultCacheKeys(['ITEMS', 'TOTAL']); // что увидит эпилог // без этого вызова эпилог получит весь результат, а кэш заметно потяжелеет $this->includeComponentTemplate(); }}Второй аргумент запуска кэша - это дополнительная часть ключа. Группы пользователя добавляют туда всегда, когда выборка зависит от прав, иначе гость получит чужую готовую разметку.
Смотрим, какой шаблон выиграл поиск:
// template.php любого шаблонаprintf("шаблон: %s из %s\n", $templateName, $templateFolder);print_r(array_keys($arResult));// путь сразу показывает, чей шаблон подключён: проекта, ядра или самого компонентаПечать пути занимает секунду и снимает половину вопросов о правках. Одноимённый шаблон в общей папке проекта выигрывает поиск и молча подменяет тот, который правит разработчик.
Правим результат до вывода:
// result_modifier.php шаблона$arResult['ITEMS'] = array_filter($arResult['ITEMS'], static fn(array $i): bool => $i['ACTIVE'] === 'Y');// файл выполняется до шаблона, и его результат попадает в кэш компонента// отложенные функции вроде установки заголовка сюда не ставят: они уедут в кэшИзменения в этом файле не видны до сброса кэша. Разработчик правит строку, обновляет страницу и видит прежнюю разметку, потому что она уже лежит готовой.
Выносим некэшируемое в эпилог:
// component_epilog.php шаблона\Bitrix\Main\Localization\Loc::loadLanguageFile(__FILE__); // автозагрузки фраз здесь нет$APPLICATION->SetTitle($arResult['TOTAL'] . ' товаров'); // ключ TOTAL объявлен для кэша// файл выполняется после шаблона и на каждом запросе, даже при попадании в кэшПередаём данные из шаблона в эпилог:
$templateData['LAST_ID'] = end($arResult['ITEMS'])['ID'] ?? 0;// массив передаётся в эпилог и кэшируется вместе с разметкой шаблонаЭтот массив решает задачу передачи данных вперёд по цепочке. Шаблон вычисляет значение один раз, а эпилог получает его готовым и не повторяет запрос к базе на каждом хите.
Помечаем область для композита:
$this->setFrameMode(true); // область считается динамической при композитном кэше// вызывают в шаблоне до вывода разметки, иначе граница области определится неверно// без пометки композит отдаёт статическую копию вместе с персональными даннымиПометка динамической области нужна не всякому компоненту. Её ставят там, где разметка зависит от текущего посетителя: корзина, личный кабинет, персональные подборки товаров.
Ограничения
Правка результата целиком живёт внутри кэша компонента. Пока кэш компонента не сброшен, изменения этого файла на витрине не появятся ни при каких условиях.
Эпилог видит только те ключи результата, которые объявлены для кэша компонента. Всё остальное для него не существует, даже если шаблон печатал эти данные строкой выше.
Языковые фразы в эпилоге шаблона не подгружаются платформой сами собой. Их подключают явно, а сами фразы кладут в файл с тем же именем внутри языковой папки шаблона.
Отложенные функции вывода в правке результата работают совсем не так, как ожидается. Заголовок страницы и метатеги ставят в эпилоге либо через отложенный вывод области.
Действия компонента не запускают его основную логику при вызове из браузера. Данные, которые действие ожидает получить из основного метода, ему придётся собрать самому.
Типичные проблемы
Правка файла шаблона не появляется на сайте.
Изменён файл правки результата, а разметка отдаётся из кэша компонента. Кэш сбрасывают либо переносят правку в эпилог, если она обязана выполняться всегда.
В эпилоге пусто там, где в шаблоне были данные.
Нужный ключ результата не объявлен в списке кэшируемых ключей компонента. Список ключей расширяют осознанно: каждый лишний ключ увеличивает размер кэша компонента.
Гость видит данные авторизованного покупателя.
В дополнительную часть ключа кэша не добавлены группы текущего пользователя. Любая зависимость выборки от прав обязана попадать в ключ кэша.
Подписи в эпилоге выводятся кодами фраз.
Языковой файл эпилога не подключён, а автозагрузки для него нет. Файл фраз подключают явным вызовом в первых строках самого эпилога.
Заголовок страницы меняется через раз.
Установка заголовка стоит в правке результата и уехала в кэш вместе с разметкой. Заголовок страницы ставят в эпилоге, который выполняется на каждом запросе.
Подключается чужой шаблон вместо своего.
Одноимённый шаблон лежит выше по порядку поиска, например в общей папке шаблонов. Путь подключённого шаблона печатают прямо из файла и сравнивают с ожидаемым.
Частые вопросы
Почему шаблон не выполняется при попадании в кэш?
Кэш хранит готовую разметку целиком, а не только данные. Платформа отдаёт её сразу, поэтому код шаблона и правка результата не запускаются.
Чем правка результата отличается от эпилога?
Первая выполняется до вывода и кэшируется вместе с разметкой, второй - после вывода на каждом запросе. Некэшируемое место всегда принадлежит эпилогу.
Что класть в дополнительную часть ключа кэша?
Всё, от чего зависит результат: группы пользователя, валюту, город, язык. Иначе разные посетители получат один и тот же готовый ответ.
Зачем прерывать кэширование при пустом результате?
Чтобы не хранить пустые страницы и не отдавать их после появления данных. Прерывание оставляет кэш чистым и ускоряет разбор проблем.
Выполняется ли логика компонента при вызове действия?
Нет, при вызове действия работают только подготовка параметров и само действие. Нужные данные действие получает своими запросами.
Смежное
- Свои компоненты - оглавление подтемы
- Свой компонент: структура, параметры, кэш результата - как написать компонент с нуля
- AJAX-действия своего компонента: действия, параметры, ошибки - режим действий целиком
- Свой компонент не подключается: разбор причин - разбор молчащего вызова
- Шаблон чужого компонента: копия, доработка результата, эпилог - правка штатного шаблона
- Правки шаблона компонента не видны: разбор причин - когда виноват кэш результата
- Путь запроса на витрине: пролог, компоненты, буфер, эпилог - сборка всей страницы целиком
- Компоненты и шаблоны - устройство компонентов целиком
- Комплексный компонент изнутри: маршруты, переменные, выбор страницы - тот же механизм у комплексного компонента