Комплексный компонент — это фактически несколько страниц под одним вызовом, и шаблон вложенного компонента об этом ничего не знает. Отсюда типовая задача: показать блок только на детальной странице, а на списке — не показывать.
Первое, что приходит в голову, — разобрать $APPLICATION->GetCurPage() регулярками. Работает до первого изменения структуры URL. Правильный способ — спросить у самого Битрикса, и делается это через CComponentEngine.
Задача
При кастомизации шаблона компонента часто нужно знать, на какой странице комплексного компонента мы находимся: на списке, в разделе или на детальной странице.
Например, в bitrix:news это могут быть:
news— главная страница спискаsection— страница разделаdetail— детальная страница элемента
Сначала — простой вариант
Прежде чем городить конструкцию с getParent(), проверьте, где именно вы находитесь. В шаблоне самого комплексного компонента текущая страница уже доступна в переменной $componentPage — никаких вычислений не требуется:
<?php // template.php комплексного компонента
if ($componentPage === 'detail') {
// ...
}Приём ниже нужен в другом случае: вы находитесь в шаблоне вложенного компонента (news.detail, breadcrumb и т. п.) и хотите узнать про страницу родителя.
Решение
Код размещается в файле result_modifier.php шаблона компонента:
<?php
// /local/templates/.default/components/bitrix/news/blog/result_modifier.php
$component = $this->getComponent();
if ($component) {
$parent = $component->getParent();
if ($parent) {
$parentParams = $parent->arParams;
$engine = new \CComponentEngine();
// $variables заполняется методом по ссылке — инициализируем явно,
// иначе на PHP 8 получим предупреждение о неопределённой переменной
$variables = [];
// Определяем текущую страницу по SEF-шаблонам
$page = $engine->guessComponentPath(
$parentParams['SEF_FOLDER'],
$parentParams['SEF_URL_TEMPLATES'],
$variables
);
$arResult['TARGET_PAGE'] = $page;
$arResult['TARGET_VARIABLES'] = $variables; // CODE элемента и раздела
}
}Проверки if ($component) и if ($parent) — не перестраховка. Тот же шаблон может быть подключён и вне комплексного компонента, и тогда getParent() вернёт null. Без проверки страница упадёт с фатальной ошибкой, причём только на одной из страниц сайта — то есть баг найдут пользователи, а не вы.
Как это работает
SEF_URL_TEMPLATES
Комплексный компонент определяет шаблоны URL в параметре SEF_URL_TEMPLATES:
$arParams['SEF_URL_TEMPLATES'] = [
'news' => '',
'section' => '#SECTION_CODE#/',
'detail' => '#SECTION_CODE#/#ELEMENT_CODE#/'
];CComponentEngine::guessComponentPath
Метод guessComponentPath сравнивает текущий URL с шаблонами и возвращает ключ соответствующего шаблона.
| URL | Результат |
|---|---|
/blog/ | news |
/blog/tutorials/ | section |
/blog/tutorials/my-first-post/ | detail |
Использование в шаблоне
После определения страницы можно менять логику отображения:
// template.php
<?php if ($arResult['TARGET_PAGE'] === 'detail'): ?>
<!-- Показываем sidebar только на детальной странице -->
<aside class="article-sidebar">
<?php $APPLICATION->IncludeComponent('bitrix:news.list', 'related', [...]); ?>
</aside>
<?php endif; ?>
<?php if ($arResult['TARGET_PAGE'] === 'news'): ?>
<!-- На главной странице показываем featured-блок -->
<div class="featured-posts">
<!-- ... -->
</div>
<?php endif; ?>Результат кэшируется вместе с шаблоном. result_modifier.php выполняется внутри кэша компонента, поэтому TARGET_PAGE попадёт в кэш и на всех страницах вернётся одно и то же значение. В этом конкретном случае обычно всё в порядке: у списка и детальной страницы разные URL, а значит и разные ключи кэша. Но если вы используете guessComponentPath для чего-то, что меняется чаще ключа кэша, результат нужно вычислять в component_epilog.php.
Альтернативный способ
Если нужно определить страницу внутри вложенного компонента (например, news.detail), можно использовать глобальную переменную:
// В комплексном компоненте (component.php)
global $APPLICATION;
$APPLICATION->SetPageProperty('COMPLEX_COMPONENT_PAGE', $componentPage);// В любом месте шаблона
$currentPage = $APPLICATION->GetPageProperty('COMPLEX_COMPONENT_PAGE');Способ рабочий, но у него есть цена: он требует правки component.php комплексного компонента, то есть его копирования в /local/. Копия ядрового компонента — это то, что придётся сопровождать вручную при каждом обновлении Битрикса. Ради определения текущей страницы такой размен обычно невыгоден; вариант через getParent() не трогает ядро вообще.
Практический пример: хлебные крошки
<?php
// result_modifier.php компонента bitrix:breadcrumb
$component = $this->getComponent();
if ($component) {
$parent = $component->getParent();
if ($parent) {
$engine = new \CComponentEngine();
$page = $engine->guessComponentPath(
$parent->arParams['SEF_FOLDER'],
$parent->arParams['SEF_URL_TEMPLATES'],
$variables
);
// На детальной странице добавляем название элемента
if ($page === 'detail' && !empty($variables['ELEMENT_CODE'])) {
// Логика добавления элемента в крошки
}
}
}Итоги
Порядок выбора, от простого к сложному:
- В шаблоне комплексного компонента — переменная
$componentPage, ничего вычислять не нужно. - В шаблоне вложенного компонента —
getParent()плюсCComponentEngine::guessComponentPath(). Не требует правок ядра и переживает изменения структуры URL, потому что читает те же настройки, по которым Битрикс сам маршрутизирует запрос. - Разбор
GetCurPage()регулярками — только если первые два варианта неприменимы. Это дублирование логики маршрутизации, и рассинхронизация с настройками компонента — вопрос времени.
Ограничение метода: он работает только с комплексными компонентами в SEF-режиме, потому что опирается на SEF_URL_TEMPLATES. Без SEF страницы различаются параметрами запроса, и определять текущую нужно по $_REQUEST.
Комментарии
Система комментариев скоро будет подключена. А пока вы можете написать мне в Telegram или на email.