Почему нужно использовать комплексные компоненты?

Комплексные компоненты необходимо использовать в случаях, когда функционал состоит из нескольких страниц с простыми компонентами.
Во-первых это более удобно использовать если вы планируете использовать ЧПУ, но только если все страницы с простыми компонентами имеют первую общую часть url. Например, страница со списком записей раздела имеет маску адреса /news/#SECTION_CODE#/, а страница детального описания имеет маску /news/#SECTION_CODE#/#ELEMENT_CODE#/, то есть неизменяемая общая часть url - это /news/
Во-вторых вы один раз при разработке комплексного компонента прописали параметры компонентов и при повторном использовании комплексного компонента, вам не нужно следить за связью настроек простых компонентов в случае если бы этот функционал был бы реализован с помощью нескольких страниц и простыми компонентами. То есть упрощает повторное использование и уменьшает шанс возникновения багов.

Основное отличие комплексного компонента от простого заключается в структуре шаблона. У простого компонента в шаблоне стандартно в большинстве случаев подключается template.php, а шаблон комплексного компонента всегда имеет более одного файла и основная задача комплексного компонента - это определение какой конкретно файл шаблона подключить в зависимости от url страницы. Сам комплексный компонент не должен выполнять бизнес-логику - за это должны отвечать простые компоненты, которые подключаются на конкретной странице шаблона.

Но чаще всего не пишут комплексные компоненты из-за проблем с реализацией роутинга. Поэтому я решил на этом акцентировать внимание.

Особенности:

  1. Включен или нет в настройках компонента режим ЧПУ ("SEF_MODE"). Важно понимать, что после включения ЧПУ в режиме редактирования и сохранения настроек компонента через интерфейс битрикса, добавляется элемент массива с чпу параметрами в массив  файл /urlrewrite.php. Поэтому если вы через ftp напрямую в коде включили/выключили чпу, то не забудьте прописать/удалить правила в /urlrewrite.php.
  2. Использование алиасов (псевдоимен). Они необходимы если отличаются названия переменных в коде компонента и в url маске в настройках компонента, то есть, чтобы не нужно было переписывать код компонента под маску url страницы. К примеру, в коде компонента вы обращаетесь к $arVariables['CODE'], а в настройках компонента вы указали /news/#SECTION_CODE#/. Поэтому мы указываем в алиасах  'CODE' => 'SECTION_CODE'.

Структура компонента

Вместо файла template.php, в папке шаблона находятся два php файла - это element.php и section.php. В зависимости от url комплексный компонент будет подключать из из этих файлов с переменными, которые найдены по url-маске.

Минимальный файл .parameters.php. Параметры компонента для ЧПУ и настройка 404:

<?php

use Bitrix\Main\Loader;

if (!defined("B_PROLOG_INCLUDED") || B_PROLOG_INCLUDED !== true) {
die();
}
/** @var array $arCurrentValues */
Loader::includeModule('iblock');

$arComponentParameters = [
"PARAMETERS" => [
"VARIABLE_ALIASES" => [//псевдоимена
"ELEMENT_CODE" => [
"NAME" => 'Символьный код элемента',
],
"SECTION_CODE" => [
"NAME" => 'Символьный код раздела',
],
],
"SEF_MODE" => [//Вкл/выкл режим ЧПУ. Каждый дочерний элемент - это шаблон, на котором подключаются простые компоненты.
"section" => [
"NAME" => 'Страница раздела',
"DEFAULT" => "#SECTION_CODE#/",
"VARIABLES" => [
"SECTION_ID",
"SECTION_CODE",
"SECTION_CODE_PATH",
],
],
"element" => [
"NAME" => 'Детальная страница',
"DEFAULT" => "#SECTION_CODE#/#ELEMENT_CODE#/",
"VARIABLES" => [
"ELEMENT_ID",
"ELEMENT_CODE",
"SECTION_ID",
"SECTION_CODE",
"SECTION_CODE_PATH",
]
]
]
]
];
//Настройки для 404.
CIBlockParameters::Add404Settings($arComponentParameters, []);

Минимальный набор для class.php. Определение какой шаблон подключить и отдача 404 если шаблон не найден.

<?php

use Bitrix\Iblock\Component\Tools;
use Bitrix\Main\Loader;

if (!defined("B_PROLOG_INCLUDED") || B_PROLOG_INCLUDED !== true) {
die();
}

class ComplexComponent extends CBitrixComponent
{
/**
* Список переменных, которых также нужно получить из $_REQUEST параметров, но их нет в url-маске
* К примеру мы указали маску /catalog/#SECTION_CODE#/ и в $arComponentVariables указали "SECTION",
* то при парсинге урл /catalog/section-code/?SECTION=123 в $arVariables будет SECTION_CODE и SECTION, несмотря на то, что SECTION в маске нет.
* @var array|string[]
*/
protected array $arComponentVariables = [
"SECTION",
];

public function executeComponent()
{
Loader::includeModule('iblock');

if ($this->arParams["SEF_MODE"] === "Y") {
$componentPage = $this->sefMode();
} else {
$componentPage = $this->noSefMode();
}

//Отдать 404 статус если не найден шаблон
if (!$componentPage) {
Tools::process404(
$this->arParams["MESSAGE_404"],
($this->arParams["SET_STATUS_404"] === "Y"),
($this->arParams["SET_STATUS_404"] === "Y"),
($this->arParams["SHOW_404"] === "Y"),
$this->arParams["FILE_404"]
);
}

$this->IncludeComponentTemplate($componentPage);
}

protected function sefMode()
{
//Значение алиасов по умолчанию.
$arDefaultVariableAliases404 = [];

/**
* Значение масок для шаблонов по умолчанию. - маски без корневого раздела,
* который указывается в $arParams["SEF_FOLDER"]
*/
$arDefaultUrlTemplates404 = [
"section" => "#SECTION_CODE#/",
"element" => "#SECTION_CODE#/#ELEMENT_CODE#/",
];

//В этот массив будут заполнены переменные, которые будут найдены по маске шаблонов url
$arVariables = [];

$engine = new CComponentEngine($this);
//Нужно добавлять для парсинга SECTION_CODE_PATH и SMART_FILTER_PATH (жадные шаблоны)
$engine->addGreedyPart("#SECTION_CODE_PATH#");
$engine->addGreedyPart("#SMART_FILTER_PATH#");
$engine->setResolveCallback(["CIBlockFindTools", "resolveComponentEngine"]);

//Объединение дефолтных параметров масок шаблонов и алиасов. Параметры из настроек заменяют дефолтные.
$arUrlTemplates = CComponentEngine::makeComponentUrlTemplates($arDefaultUrlTemplates404, $this->arParams["SEF_URL_TEMPLATES"]);
$arVariableAliases = CComponentEngine::makeComponentVariableAliases($arDefaultVariableAliases404, $this->arParams["VARIABLE_ALIASES"]);

//Поиск шаблона
$componentPage = $engine->guessComponentPath(
$this->arParams["SEF_FOLDER"],
$arUrlTemplates,
$arVariables
);

//Проброс значений переменных из алиасов.
CComponentEngine::initComponentVariables($componentPage, $this->arComponentVariables, $arVariableAliases, $arVariables);
$this->arResult = [
"VARIABLES" => $arVariables,
"ALIASES" => $arVariableAliases
];

return $componentPage;
}

protected function noSefMode()
{
$componentPage = "";
$arVariables = [];
$arDefaultVariableAliases = [];

//Объединение дефолтных алиасов. Параметры из настроек заменяют дефолтные.
$arVariableAliases = CComponentEngine::makeComponentVariableAliases($arDefaultVariableAliases, $this->arParams["VARIABLE_ALIASES"]);
//Получаем значения переменных в $arVariables
CComponentEngine::initComponentVariables(false, $this->arComponentVariables, $arVariableAliases, $arVariables);

//По найденным параметрам $arVariables определяем тип страницы
if ((isset($arVariables["ELEMENT_ID"]) && intval($arVariables["ELEMENT_ID"]) > 0)
|| (isset($arVariables["ELEMENT_CODE"]) && $arVariables["ELEMENT_CODE"] <> '')
) {
$componentPage = "element";
} elseif ((isset($arVariables["SECTION_ID"]) && intval($arVariables["SECTION_ID"]) > 0)
|| (isset($arVariables["SECTION_CODE"]) && $arVariables["SECTION_CODE"] <> '')
) {
$componentPage = "section";
}

$this->arResult = [
"VARIABLES" => $arVariables,
"ALIASES" => $arVariableAliases
];

return $componentPage;
}
}
Теперь если подключим компонент на странице /test/index.php
<?
require($_SERVER["DOCUMENT_ROOT"]."/bitrix/header.php");
$APPLICATION->SetTitle("Тест использования комплексного компонента");
?><?$APPLICATION->IncludeComponent(
"local:catalog",
".default",
array(
"SEF_FOLDER" => "/test/",
"SEF_MODE" => "Y",
"COMPONENT_TEMPLATE" => ".default",
"SET_STATUS_404" => "Y",
"SHOW_404" => "Y",
"MESSAGE_404" => "Не найдено",
"SEF_URL_TEMPLATES" => array(
"section" => "#SECTION_CODE#/",
"element" => "#SECTION_CODE#/#ELEMENT_CODE#/",
)
),
false
);?><?require($_SERVER["DOCUMENT_ROOT"]."/bitrix/footer.php");?>
Не забываем, что если подключаем не через интерфейс битрикса, то нужно в urlrewrite.php прописать правило чпу:
array (
'CONDITION' => '#^/test/#',
'RULE' => '',
'ID' => 'local:catalog',
'PATH' => '/test/index.php',
'SORT' => 100,
),
Теперь по адресу /test/section-code/ - подключается файл шаблона section.php с переменными:
Array(
    [VARIABLES] => Array
        (
            [SECTION_CODE] => section-code
        )

    [ALIASES] => Array
        (
        )

)
По адресу /test/section-code/element-code/ - подключается файл шаблона element.php с переменными:
Array(
    [VARIABLES] => Array
        (
            [SECTION_CODE] => section-code
            [ELEMENT_CODE] => element-code
        )

    [ALIASES] => Array
        (
        )

)
По адресам, которые не подходят под url-маски, будет показана 404 страница.

Возврат к списку