Перейти к содержимому

Комплексный компонент изнутри - маршруты, переменные, выбор страницы

Разбираем комплексный компонент по слоям: как путь запроса сопоставляется с шаблонами адресов и откуда берутся переменные. Дальше смотрим, как код страницы превращается в файл и что достаётся вложенным компонентам.

Механика

Простой компонент выводит одну область на одном физическом адресе сайта. Комплексный реализует целый раздел набором виртуальных страниц поверх одной физической страницы. Список, раздел и детальную карточку рисует один и тот же вызов, выбирая режим по адресу.

Признак комплексного компонента объявляется ключом COMPLEX в файле описания. Он же меняет форму настроек: появляются группы управления адресами страниц и настроек ЧПУ, которых у простого компонента нет.

Режимов адресации ровно два, и выбор режима меняет всю дальнейшую маршрутизацию. Без ЧПУ ссылки собираются с параметрами запроса вида /cat.php?IBLOCK_ID=12&SECTION_ID=371. В режиме ЧПУ адреса строятся по шаблонам, и компоненту нужны корневая папка и набор этих шаблонов.

Корневая папка задаётся отдельным параметром и может отличаться от физического пути файла. Все шаблоны адресов считаются именно от неё, поэтому пустой шаблон означает саму папку и открывает список.

Набор шаблонов собирается из двух источников до разбора пути. Значения по умолчанию описаны в самом компоненте, а параметры вызова переопределяют их по ключам. Ключ массива - это код страницы, значение - образец пути с местами под переменные.

Разбор адреса делает движок компонентов, а не веб-сервер и не правило обработки адресов. Правило довело запрос до физического файла, дальше путь сопоставляется с шаблонами уже внутри компонента. Результатом становятся код подходящей страницы и массив переменных, выделенных из пути.

Шаблоны проверяются как единый набор, и два образца одинаковой формы неразрешимы. Адрес /news/sport/ подходит и под шаблон раздела, и под шаблон элемента, когда оба записаны одним сегментом. Различить их платформе нечем, и это первая причина работающего списка при мёртвой детальной странице.

В режиме без ЧПУ переменные приходят не из пути, а из запроса. Их восстанавливает отдельный вызов, и он же применяет псевдонимы имён. Псевдоним связывает имя переменной комплексного компонента с именем, под которым её ждёт вложенный.

Код страницы превращается в имя файла внутри папки шаблона комплексного компонента. Подключение шаблона получает этот код аргументом, а рядом лежат файлы страниц: список, раздел, детальная. Отдельных страниц под них в структуре сайта не существует и создавать их не нужно.

Вложенные компоненты сами по себе переменных адреса не видят. Комплексный передаёт их обычными параметрами вызова и обязательно указывает себя родителем. Без родителя шаблон вложенного компонента ищется вне состава комплексного и берётся не тот.

Разбор адреса выполняется на каждом запросе и кешированию не подлежит. Кешируется вывод вложенных компонентов, и у каждого свой ключ из имени, шаблона и параметров. Поэтому очистка кеша затрагивает набор файлов, а не один общий кеш комплексного компонента.

Шаги

  1. Определить режим адресации и корневую папку компонента, от которой считаются все шаблоны.
  2. Собрать полный набор шаблонов адресов: значения по умолчанию плюс переопределения из параметров.
  3. Разобрать путь запроса и получить код страницы вместе с массивом переменных адреса.
  4. Дополнить переменные значениями из запроса, применив псевдонимы имён вложенных компонентов.
  5. Подключить файл страницы по её коду и передать переменные вложенным компонентам вызова.

Код

Собираем набор шаблонов адресов:

$arDefaultUrlTemplates404 = [
'sections' => '', // список разделов: сама папка
'section' => '#SECTION_CODE#/', // раздел
'element' => '#SECTION_CODE#/#ELEMENT_CODE#/', // детальная страница
];
// значения по умолчанию живут в самом компоненте и задают минимальный набор
$arUrlTemplates = CComponentEngine::MakeComponentUrlTemplates(
$arDefaultUrlTemplates404, $arParams['SEF_URL_TEMPLATES']);
// на выходе полный набор: ключ - код страницы, значение - образец пути

Параметры вызова переопределяют значения по умолчанию по ключам, а недостающие ключи остаются прежними. Ключ здесь - это код страницы, и он же потом выберет файл внутри шаблона компонента.

Готовим псевдонимы переменных:

$arDefaultVariableAliases404 = [];
$arVariableAliases = CComponentEngine::MakeComponentVariableAliases(
$arDefaultVariableAliases404, $arParams['VARIABLE_ALIASES']);
// 'element' => ['SECTION_ID' => 'SID', 'ELEMENT_ID' => 'ID']
// слева имя переменной компонента, справа имя, под которым она придёт в запросе

Псевдонимы нужны там, где вложенные компоненты ждут разные имена одной величины. Задаются они постранично, поэтому один идентификатор на разных страницах компонента может называться по-разному.

Разбираем путь запроса:

$arVariables = [];
$componentPage = CComponentEngine::ParseComponentPath(
$arParams['SEF_FOLDER'], // корневая папка компонента
$arUrlTemplates, // весь набор шаблонов сразу
$arVariables // части пути, переменная передаётся по ссылке
);
// шаблоны проверяются все сразу, а не по одному в порядке объявления
// в $arVariables лягут только те имена, что описаны решётками в шаблоне

Возвращается код подходящей страницы, а переменные заполняются по ссылке. Пустой код означает, что ни один шаблон не подошёл, и решение о содержимом принимает уже сам компонент по своим параметрам.

Восстанавливаем переменные из запроса:

CComponentEngine::InitComponentVariables($componentPage, $arComponentVariables,
$arVariableAliases, $arVariables);
// первым аргументом false, когда ЧПУ выключен: страницу определять не по чему
// $arComponentVariables - список имён, которые компонент готов принять из запроса

Вызов работает в обоих режимах адресации и добирает недостающие значения из запроса. Благодаря ему одна и та же страница компонента обслуживает и ЧПУ, и адреса с параметрами.

Смотрим, что вышло из адреса:

echo $componentPage ?: 'ни один шаблон не совпал';
print_r($arVariables);
// пустой код страницы при верном на вид адресе - расхождение ссылки и шаблона
// пустой массив переменных при непустом коде - шаблон без мест под переменные
// обе строки убирают сразу после разбора: это отладка, а не рабочий код

Две строки отвечают на главный вопрос разбора: какой именно шаблон поймал адрес и что он выделил из пути.

Подключаем файл страницы:

// код страницы пришёл из разбора адреса и стал именем файла шаблона
$this->IncludeComponentTemplate($componentPage); // sections, section, element
// файлы страниц лежат в папке шаблона комплексного компонента рядом друг с другом
// в структуре сайта отдельных файлов под раздел и элемент нет

Передаём переменные вложенному компоненту:

$APPLICATION->IncludeComponent('bitrix:news.detail', '', [
'IBLOCK_ID' => $arParams['IBLOCK_ID'],
'ELEMENT_ID' => $arResult['VARIABLES']['ELEMENT_ID'], // из разобранного адреса
'CACHE_TIME' => $arParams['CACHE_TIME'],
], $component); // родитель обязателен: иначе шаблон ищется вне состава комплексного
// имена ключей диктует вложенный компонент, а не комплексный
// значения из адреса лежат в $arResult['VARIABLES'] под именами шаблона

Вложенный компонент про адрес не знает ничего и получает готовые значения. Четвёртый аргумент связывает его с родителем, и вместе с этой связью работает штатный AJAX комплексного компонента.

Строим ссылку обратно по шаблону:

$url = CComponentEngine::MakePathFromTemplate(
$arParams['SEF_FOLDER'] . $arUrlTemplates['element'],
['SECTION_CODE' => 'sport', 'ELEMENT_CODE' => 'match-itogi']);
// подстановка идёт по именам в решётках, а не склейкой строк
// корневая папка приклеивается к шаблону: сам шаблон считается от неё
// правка шаблона адреса меняет и разбор пути, и все собранные ссылки сразу

Сборка и разбор адреса опираются на один и тот же набор шаблонов. Ручная склейка строк живёт до первой правки шаблона и после неё молча начинает давать битые ссылки по всему разделу.

Ограничения

Комплексный компонент не ставят в шаблон сайта, ни в шапку, ни в подвал. У него собственные правила обработки адресов, и в шаблоне они ломают маршрутизацию остальных страниц сайта.

Для нового кода платформа рекомендует контроллер, а комплексные компоненты не создавать. Разбираться в их устройстве всё равно приходится: штатные каталог и новости построены именно так.

Два шаблона одинаковой формы неразрешимы в принципе, и параметрами это не обходится. Адреса раздела и элемента различают глубиной пути или отдельным постоянным сегментом внутри шаблона.

Шаблон вложенного компонента внутри комплексного не подхватится под именем по умолчанию. Одному и тому же компоненту отдельно и в составе комплексного дают разные имена шаблонов.

Разбор адреса идёт на каждом хите, а кешируется только вывод вложенных компонентов. Правка их шаблона при включённом кешировании не видна до очистки кеша из административной панели.

Типичные проблемы

Список открывается, детальная страница отдаёт 404.

Шаблоны раздела и элемента записаны одной формой, и адрес подходит сразу под оба. Платформе нечем их различить, поэтому в шаблон детальной страницы добавляют код раздела.

Лишние символы в конце адреса открывают обычную страницу.

Шаблон совпал с началом пути, а его хвост в переменные не попал и потерялся. Строгую проверку раздела и отдачу ошибки включают параметрами самого компонента.

Вложенный компонент не видит кода элемента из адреса.

Переменная не передана ему параметром вызова из разобранного массива адреса. Комплексный компонент раздаёт значения вручную, автоматической передачи между компонентами нет.

Правки шаблона внутри комплексного не подхватываются.

Шаблон вложенного компонента назван по умолчанию и совпал с шаблоном отдельного вызова. Внутри комплексного ему дают собственное имя, отличное от значения по умолчанию.

Умный фильтр внутри комплексного строит адрес с мусором.

В наборе шаблонов нет ключа под страницу фильтра, и путь собирается из чего попало. Страница фильтра описывается своим шаблоном адреса наравне с разделом и элементом.

Ссылки ломаются после правки шаблонов адресов.

Они собраны склейкой строк прямо в шаблоне, а не подстановкой в шаблон адреса. Сборку ссылок ведут тем же набором шаблонов, которым идёт разбор пути.

Частые вопросы

Чем комплексный компонент отличается от простого?

Простой выводит одну область на одном адресе, комплексный - целый раздел виртуальными страницами. Маршрут по адресу он разбирает сам, без отдельных файлов под каждую страницу.

Где лежат файлы страниц комплексного компонента?

В папке его шаблона, рядом друг с другом: список, раздел и детальная страница. Оттуда подключается файл по коду страницы, полученному при разборе адреса.

Почему детальная страница не работает, а список открывается?

Чаще всего шаблоны раздела и элемента имеют одинаковую форму пути. Адрес подходит под оба, и компонент выбирает не ту страницу, которую ждут.

Зачем нужны псевдонимы переменных?

Они связывают имена переменных комплексного компонента с именами, которых ждут вложенные. Задаются постранично, поэтому одна величина на разных страницах может называться по-разному.

Стоит ли писать свой комплексный компонент?

Для нового кода платформа советует контроллер, а комплексные компоненты не создавать. Разбираться в устройстве всё равно приходится: штатные каталог и новости построены так.

Смежное

Первоисточник