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

URL: https://dmeshcheryakov.ru/blog/bitrix-complex-component-page
Раздел: 1С-Битрикс
Теги: 1С-Битрикс, Компоненты, PHP
Опубликовано: 2024-12-24
Обновлено: 2026-09-07
Автор: Дмитрий Мещеряков (https://dmeshcheryakov.ru)

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

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

Первое, что приходит в голову, — разобрать `$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` |

### Что попадает в `$variables`

Третий аргумент `guessComponentPath()` заполняется по ссылке и содержит значения плейсхолдеров из совпавшего шаблона URL. Для страницы `detail` с шаблоном `#SECTION_CODE#/#ELEMENT_CODE#/` и адреса `/blog/tutorials/my-first-post/` там окажется:

```php
[
    'SECTION_CODE' => 'tutorials',
    'ELEMENT_CODE' => 'my-first-post',
]
```

Это готовые значения для выборки: не нужно ни разбирать URL, ни обращаться к `$_REQUEST`. Учтите только, что ключи соответствуют плейсхолдерам конкретного комплексного компонента — у `catalog` они отличаются от `news`, и сверяться нужно с его `SEF_URL_TEMPLATES`, а не с памятью.

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

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

```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`.