1С Битрикс · 6 мин чтения

Передать параметр из одного компонента в другой в Битрикс

Передать параметр из одного компонента в другой в Битрикс не получится обычным присваиванием переменной в шаблоне: движок рендерит каждый компонент в собственном PHP-контексте, и массив $arResult одного инстанса недоступен другому напрямую. На практике я регулярно связываю фильтр каталога с блоком корзины, виджет выбора города с калькулятором доставки, шапку сайта с блоком акций. Ниже - четыре рабочих способа передачи данных между независимыми компонентами: через $arResult и вложенный include, через глобальные переменные, через GET-параметры в адресной строке и через буфер $APPLICATION вместе с сессией. Отдельно разбираю кэш компонентов, из-за которого переданное значение чаще всего пропадает без видимой причины.

Почему компоненты в Битриксе не видят переменные друг друга

Каждый вызов $APPLICATION->IncludeComponent() запускает отдельный экземпляр компонента: свои $arParams, свой $arResult, свой template.php. Соседний компонент на той же странице ничего об этом не знает, у него собственный набор переменных, и прямого моста между ними ядро не предусматривает.

Исключение - вложенность. Если компонент A внутри своего шаблона вызывает компонент B, он может передать B любые данные из своего $arResult через параметры вызова. Это работает только для связки «родитель - потомок». Для двух независимых компонентов, подключённых рядом через шаблон сайта или пункты меню, нужен один из способов ниже.

Способ 1. Передать параметр через $arResult вложенного компонента

Самый предсказуемый вариант, когда один компонент физически вызывает другой. Например, в шаблоне catalog.section я беру ID раздела из $arResult и передаю его в маленький компонент вывода баннера или счётчика товаров.

<?
$APPLICATION->IncludeComponent(
	"bitrix:sale.basket.basket.line",
	".default",
	array(
		"PATH_TO_BASKET" => "/personal/cart/",
		"SECTION_ID" => $arResult["SECTION"]["ID"],
		"SHOW_NUM_PRODUCTS" => "Y",
	)
);
?>

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

Бесплатный материал

🎁 Полезный скрипт в подарок

Подпишитесь на Telegram - пришлю готовый скрипт по этой теме.

Без спама. Отписка в 1 клик.

Способ 2. Глобальные переменные между независимыми компонентами

Если компоненты не связаны вложенностью, но подключаются в одном хите один за другим (оба лежат в шаблоне сайта, например в header и в области контента), можно прокинуть значение через $GLOBALS. Первый компонент кладёт значение в глобальную область, второй читает его. Порядок подключения обязателен: тот, кто пишет, должен отработать раньше того, кто читает.

// component.php первого компонента
$GLOBALS['SELECTED_CITY_ID'] = $arResult['CITY_ID'];

// component.php второго компонента
global $SELECTED_CITY_ID;
if (!empty($SELECTED_CITY_ID)) {
	$arParams['CITY_ID'] = $SELECTED_CITY_ID;
}

На практике так я связываю выбор города в шапке с ценами в каталоге, когда переписывать типовые компоненты под собственный API долго. Минус способа в хрупкости: он завязан на порядок подключения в шаблоне сайта, и перестановка блоков в визуальном редакторе способна тихо сломать связь. Второй минус - кэш: если компонент, который пишет в $GLOBALS, отдаёт результат из кэша, код внутри component.php вообще не выполняется, и переменная не появится. Подробнее - в разделе про кэш ниже.

Способ 3. GET-параметры для передачи между компонентами

Через адресную строку значение долетает куда угодно, в том числе между компонентами на разных страницах, именно так устроены фильтры каталога, пагинация и выбор раздела в большинстве типовых решений Битрикс. Кладёте параметр в ссылку, оба компонента читают его через $_REQUEST.

$cityId = (int)($_REQUEST['CITY_ID'] ?? 0);
if ($cityId > 0) {
	$arFilter['PROPERTY_CITY'] = $cityId;
}
// ссылка вида /catalog/?CITY_ID=77

У способа два практических правила. Значение из $_REQUEST всегда привожу к нужному типу перед использованием в запросе к базе, (int) для ID, отдельная проверка через регулярку для строк, иначе получите классическую SQL-инъекцию через параметр фильтра. И второе: GET переживает переход на другую страницу, но исчезает, если пользователь наберёт адрес заново или уберёт параметр из ссылки. Для долгоживущего состояния, вроде выбранного города на весь визит, лучше сочетать GET с сессией, записывая туда значение при первом обращении.

Способ 4. Буфер $APPLICATION и сессия

$APPLICATION->Set() и $APPLICATION->Get() - штатный механизм ядра именно для передачи данных между компонентами одного хита, тем же способом Битрикс прокидывает заголовок страницы и хлебные крошки. Он надёжнее самодельных $GLOBALS, потому что не зависит от того, объявлена ли переменная global в нужном месте.

// в компоненте-источнике
$APPLICATION->Set('SELECTED_CITY_ID', $arResult['CITY_ID']);

// в любом компоненте, подключённом позже
$cityId = $APPLICATION->Get('SELECTED_CITY_ID');

Для данных, которые должны пережить переход на другую страницу и которые не хочется тащить в каждой ссылке, использую $_SESSION: выбранный филиал доставки, валюту показа цен. Записываю значение один раз в компоненте выбора, читаю в любом другом компоненте на любой странице сайта. Осторожно с объёмом: если в сессию попадает не ID, а весь массив товаров или большая выборка из инфоблока, файл сессии распухает, и каждый хит начинает читать его целиком, это заметно на нагруженном каталоге.

Способ Переживает переход на другую страницу Требует правки чужого компонента
$arResult + вложенный include Нет Да, нужен доступ к шаблону родителя
Глобальные переменные / $GLOBALS Нет Да, нужен доступ к component.php
GET-параметры Да, пока параметр есть в ссылке Нет
$APPLICATION->Set/Get и сессия Да для сессии, нет для буфера Set/Get Нет

Кэш компонента: почему переданное значение вдруг пропадает

Самая частая причина, по которой все четыре способа выше вдруг «перестают работать», это кэш. Если у компонента-источника выставлен CACHE_TYPE => 'Y' или 'A' и по текущим параметрам уже есть закэшированный результат, Битрикс отдаёт готовый HTML и не выполняет PHP-код component.php вообще, а значит ни $GLOBALS, ни $APPLICATION->Set() внутри него не сработают.

Три рабочих варианта обойти это:

  • Отключить кэш для конкретного компонента ($arParams['CACHE_TYPE'] = 'N'), если он лёгкий и не нагружает базу.
  • Перенести код, который отдаёт значение наружу, в result_modifier.php: этот файл выполняется при каждом хите независимо от того, закэширован ли шаблон компонента.
  • Записывать значение до вызова компонента, в шаблоне сайта или в обработчике события OnProlog в init.php, а не рассчитывать, что это сделает сам закэшированный компонент.

Когда связка кэшированных типовых компонентов и кастомной логики начинает жить своей жизнью после каждого обновления модуля, обычно проще не гадать с global-переменными, а сразу разложить это через result_modifier.php и события. Этим я и занимаюсь в рамках поддержки и сопровождения сайтов. Дальнейшую поддержку таких связок обычно оформляю как техподдержку от 15 000 ₽/мес, а не как разовую правку: после каждого обновления модуля или ядра эти места первыми ловят регресс.

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

Можно ли передать параметр между компонентами на разных страницах?

Да, для этого подходит GET, если пользователь переходит по конкретной ссылке, или сессия, если значение должно сохраниться без передачи в адресе. $arResult и глобальные переменные работают только внутри одного хита и до следующей страницы не доживают.

Почему global-переменная из одного компонента не видна в другом на той же странице?

Чаще всего компонент-источник закэширован, и на cache hit его component.php не выполняется вообще, а значит и код, записывающий $GLOBALS, не срабатывает. Проверьте это первым делом: временно отключите кэш компонента (CACHE_TYPE => 'N') и посмотрите, появится ли переменная.

Как передать значение в типовой компонент, код которого лучше не трогать?

Через $APPLICATION->Set() перед вызовом IncludeComponent или через GET-параметр. Оба способа не требуют правки самого компонента, только шаблона, откуда вы его вызываете.

Стоит ли хранить в сессии крупные объёмы данных для передачи между компонентами?

Нет, в сессию имеет смысл класть только идентификаторы: ID города, филиала, валюты, а не массивы товаров или результаты выборок из инфоблоков. Иначе файл сессии растёт, и каждый хит становится медленнее.

Есть задача?

Обсудим в мессенджере

Расскажите, что нужно сделать — отвечу в течение 4 часов в рабочее время. Первая консультация бесплатно.

Продолжая пользование настоящим сайтом Вы выражаете своё согласие на обработку Ваших персональных данных (файлов куки) с использованием Yandex.Metrika.
Понятно