Главная/Статьи/Битрикс: определение текущей страницы комплексного компонента

Битрикс: определение текущей страницы комплексного компонента

Как программно определить, на какой странице комплексного компонента мы находимся. Используем CComponentEngine для работы с SEF-шаблонами.

ДМ
Дмитрий Мещеряков
📅 24 декабря 2024 г.📖 4 мин чтения

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

Первое, что приходит в голову, — разобрать $APPLICATION->GetCurPage() регулярками. Работает до первого изменения структуры URL. Правильный способ — спросить у самого Битрикса, и делается это через CComponentEngine.

Задача

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

Например, в bitrix:news это могут быть:

  • news — главная страница списка
  • section — страница раздела
  • detail — детальная страница элемента

Сначала — простой вариант

Прежде чем городить конструкцию с getParent(), проверьте, где именно вы находитесь. В шаблоне самого комплексного компонента текущая страница уже доступна в переменной $componentPage — никаких вычислений не требуется:

php
<?php // template.php комплексного компонента
if ($componentPage === 'detail') {
    // ...
}

Приём ниже нужен в другом случае: вы находитесь в шаблоне вложенного компонента (news.detail, breadcrumb и т. п.) и хотите узнать про страницу родителя.

Решение

Код размещается в файле result_modifier.php шаблона компонента:

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:

php
$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

Использование в шаблоне

После определения страницы можно менять логику отображения:

php
// 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), можно использовать глобальную переменную:

php
// В комплексном компоненте (component.php)
global $APPLICATION;
$APPLICATION->SetPageProperty('COMPLEX_COMPONENT_PAGE', $componentPage);
php
// В любом месте шаблона
$currentPage = $APPLICATION->GetPageProperty('COMPLEX_COMPONENT_PAGE');

Способ рабочий, но у него есть цена: он требует правки component.php комплексного компонента, то есть его копирования в /local/. Копия ядрового компонента — это то, что придётся сопровождать вручную при каждом обновлении Битрикса. Ради определения текущей страницы такой размен обычно невыгоден; вариант через getParent() не трогает ядро вообще.

Практический пример: хлебные крошки

php
<?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'])) {
            // Логика добавления элемента в крошки
        }
    }
}

Итоги

Порядок выбора, от простого к сложному:

  1. В шаблоне комплексного компонента — переменная $componentPage, ничего вычислять не нужно.
  2. В шаблоне вложенного компонентаgetParent() плюс CComponentEngine::guessComponentPath(). Не требует правок ядра и переживает изменения структуры URL, потому что читает те же настройки, по которым Битрикс сам маршрутизирует запрос.
  3. Разбор GetCurPage() регулярками — только если первые два варианта неприменимы. Это дублирование логики маршрутизации, и рассинхронизация с настройками компонента — вопрос времени.
💡 Совет

Ограничение метода: он работает только с комплексными компонентами в SEF-режиме, потому что опирается на SEF_URL_TEMPLATES. Без SEF страницы различаются параметрами запроса, и определять текущую нужно по $_REQUEST.

🚀

Хотите такое же решение?

Настрою окружение под ваш проект, учту специфику инфраструктуры и обучу команду.

Обсудить проект →
Бесплатная консультация · Ответ в течение дня

Комментарии

Система комментариев скоро будет подключена. А пока вы можете написать мне в Telegram или на email.