Механизм ResultModifier в 1С-Битрикс: настройка вывода компонента

Раздел: 1С-Битрикс -> Bitrix разработка

Модификатор результата в Bitrix: назначение и варианты реализации

Модификатор результата (ResultModifier) в 1С-Битрикс предназначен для изменения массива $arResult непосредственно перед его передачей в шаблон компонента. Это позволяет адаптировать выходные данные без изменения исходного кода компонента или его шаблона. Основные цели: добавление вычисляемых полей, фильтрация элементов, объединение данных из разных источников, изменение структуры для удобства верстки.

Как добавить дополнительные поля в результат компонента новостей?

Стандартный способ разместить в папке шаблона компонента файл result_modifier.php. Битрикс автоматически выполняет его после выборки данных, но до вызова шаблона.

Пример для компонента bitrix:news.list. Допустим, нужно добавить к каждому элементу ссылку на детальную страницу с UTM-метками:

<?php
if (!defined('B_PROLOG_INCLUDED') || B_PROLOG_INCLUDED!==true) die();

foreach ($arResult['ITEMS'] as &$item) {
    $item['DETAIL_PAGE_URL_UTM'] = $item['DETAIL_PAGE_URL'] . '?utm_source=site&utm_medium=link';
}
unset($item);
?>

Bitrix result modifier php (модификатор результата bitrix)

После этого в шаблоне можно обращаться к $arItem['DETAIL_PAGE_URL_UTM']. Важно не забыть unset($item) после цикла по ссылке, чтобы не повредить последующие итерации.

Типичные ошибки:

  • Файл назван неверно: должно быть именно result_modifier.php (строчные латинские буквы, символ подчеркивания).
  • Изменение ключей массива $arResult, ожидаемых шаблоном (например, удаление поля ID) приводит к ошибкам в шаблоне.
  • Игнорирование кеширования: если компонент кешируется, модификатор выполняется только при сборке кеша. Для динамических данных нужно или отключать кеш, или использовать собственные механизмы.

Как изменить результат через событие OnBeforeResultModifier?

Если требуется модифицировать результат для всех шаблонов компонента (или группы), удобно использовать событие OnBeforeResultModifier. Подписка в init.php:

\Bitrix\Main\EventManager::getInstance()->addEventHandler(
    'main',
    'OnBeforeResultModifier',
    'modifyNewsListResult'
);

function modifyNewsListResult(\Bitrix\Main\Event $event) {
    $params = $event->getParameters();
    $component = $params[0]; // объект компонента
    $arResult = &$params[1];  // ссылка на результат
    $arParams = $params[2];    // параметры компонента
    
    if ($component->getName() === 'bitrix:news.list') {
        foreach ($arResult['ITEMS'] as &$item) {
            $item['NAME'] = mb_strtoupper($item['NAME']);
        }
        unset($item);
    }
}

Цель: централизованная обработка без изменения файлов шаблона.

Проблема: обработчик вызывается для всех компонентов, поэтому необходима проверка $component->getName(). Если проверка отсутствует, можно испортить данные других компонентов.

Как добавить в результат данные из другого инфоблока с помощью API в result_modifier.php?

Внутри result_modifier.php разрешено использовать любые API. Например, для компонента bitrix:news можно подгрузить список связанных новостей:

if (!defined('B_PROLOG_INCLUDED') || B_PROLOG_INCLUDED!==true) die();

CModule::IncludeModule('iblock');

$linkedIblockId = 5;
$elementId = $arResult['ID'];

$linked = CIBlockElement::GetList(
    ['SORT' => 'ASC'],
    ['IBLOCK_ID' => $linkedIblockId, 'ACTIVE' => 'Y', 'PROPERTY_LINK' => $elementId],
    false,
    ['nTopCount' => 3],
    ['ID', 'NAME', 'DETAIL_PAGE_URL']
);
$arResult['LINKED_ITEMS'] = [];
while ($item = $linked->Fetch()) {
    $arResult['LINKED_ITEMS'][] = $item;
}

После этого в шаблоне появляется массив $arResult['LINKED_ITEMS'].

Ошибка: если не подключить модуль iblock через CModule::IncludeModule, вызов его методов приведет к фатальной ошибке.

Как изменить структуру результата для кастомного шаблона?

Иногда требуется не добавлять поля, а полностью переформатировать $arResult. Например, сгруппировать элементы по дате:

if (!defined('B_PROLOG_INCLUDED') || B_PROLOG_INCLUDED!==true) die();

$grouped = [];
foreach ($arResult['ITEMS'] as $item) {
    $dateKey = $item['ACTIVE_FROM'] ? date('Y-m', strtotime($item['ACTIVE_FROM'])) : 'no-date';
    $grouped[$dateKey][] = $item;
}
$arResult['ITEMS_GROUPED'] = $grouped;

Шаблон использует $arResult['ITEMS_GROUPED'], а исходный $arResult['ITEMS'] сохраняется для обратной совместимости.

Каждый из вариантов решает конкретную задачу. Выбор зависит от необходимости многократного использования, уровня контроля и производительности. Для единичных правок удобен result_modifier.php шаблона, для глобальных изменений - событие.

Расширенные примеры использования модификатора результата

Ниже приведены подробные сценарии с кодом и ожидаемым результатом. Каждый пример демонстрирует типовую задачу и способы ее решения.

Пример 1: Добавление пользовательских свойств (UF) в результат компонента каталога

Задача: в компоненте bitrix:catalog (список элементов) отсутствуют некоторые UF-поля. Через result_modifier.php добавим их.

Пример
<?php
if (!defined('B_PROLOG_INCLUDED') || B_PROLOG_INCLUDED!==true) die();

CModule::IncludeModule('iblock');

// Получаем UF-поля для элементов инфоблока
$entityId = 'IBLOCK_' . $arParams['IBLOCK_ID'] . '_SECTION';
$userFields = $GLOBALS['USER_FIELD_MANAGER']->GetUserFields($entityId);

foreach ($arResult['ITEMS'] as &$item) {
    $item['UF_FIELDS'] = [];
    foreach ($userFields as $fieldName => $field) {
        $item['UF_FIELDS'][$fieldName] = $item['PROPERTIES'][$fieldName]['VALUE'] ?? '';
    }
}
unset($item);
?>

Результат: каждый элемент теперь содержит массив UF_FIELDS со значениями пользовательских полей. Вывод в шаблоне:

[ITEM] => [
    [ID] => 123
    [NAME] => Товар
    [UF_FIELDS] => [
        [UF_PRICE] => 2500
        [UF_BRAND] => Sony
    ]
]

Пример 2: Фильтрация элементов по свойству, не доступному в параметрах компонента

Допустим, нужно вывести только те новости, у которых свойство IS_MAIN равно Y. В компоненте bitrix:news.list нет соответствующего фильтра. Модификатор удаляет ненужные элементы:

Пример
if (!defined('B_PROLOG_INCLUDED') || B_PROLOG_INCLUDED!==true) die();

foreach ($arResult['ITEMS'] as $key => $item) {
    if ($item['PROPERTIES']['IS_MAIN']['VALUE'] !== 'Y') {
        unset($arResult['ITEMS'][$key]);
    }
}
$arResult['ITEMS'] = array_values($arResult['ITEMS']); // переиндексация

Результат: в массиве остаются только элементы с IS_MAIN = Y. Важно переиндексировать массив, чтобы избежать пропусков ключей.

Пример 3: Добавление постраничной навигации на основе другого запроса

Иногда требуется подмешать в результат компонента собственную пагинацию, например, для элементов из соседнего инфоблока. Используем CPHPOption или прямое создание объекта CDBResult:

Пример
if (!defined('B_PROLOG_INCLUDED') || B_PROLOG_INCLUDED!==true) die();

CModule::IncludeModule('iblock');

$nav = new \Bitrix\Main\UI\PageNavigation('nav-clients');
$nav->allowAllRecords(true)->setPageSize(10)->initFromUri();

$query = CIBlockElement::GetList(
    ['SORT' => 'ASC'],
    ['IBLOCK_ID' => 7, 'ACTIVE' => 'Y'],
    false,
    $nav->getNavParams(),
    ['ID', 'NAME', 'PREVIEW_TEXT']
);

$items = [];
while ($el = $query->Fetch()) {
    $items[] = $el;
}
$arResult['ADDITIONAL_ITEMS'] = $items;
$arResult['ADDITIONAL_NAV'] = $nav;

В шаблоне выводится навигация через $arResult['ADDITIONAL_NAV'].

Пример 4: Объединение данных из нескольких копий одного компонента

На странице могут быть два вызова bitrix:news.list с разными фильтрами. Требуется объединить их результаты в один массив. Решение через событие OnBeforeResultModifier с подсчетом вызовов:

Пример
$callCounter = 0;

function mergeNewsLists(\Bitrix\Main\Event $event) {
    global $callCounter;
    $params = $event->getParameters();
    $arResult = &$params[1];
    $componentName = $params[0]->getName();
    
    if ($componentName === 'bitrix:news.list') {
        $callCounter++;
        if ($callCounter === 1) {
            // сохраняем первый результат
            $GLOBALS['firstListResult'] = $arResult['ITEMS'];
        } elseif ($callCounter === 2) {
            // сливаем со вторым
            $arResult['ITEMS'] = array_merge($GLOBALS['firstListResult'], $arResult['ITEMS']);
        }
    }
}

Этот подход хрупкий, но демонстрирует возможности событийной модели.

Пример 5: Изменение URL через result_modifier.php для компонента bitrix:menu

Компонент bitrix:menu формирует массив пунктов. Допустим, требуется добавить к ссылкам параметр ?from=menu:

Пример
if (!defined('B_PROLOG_INCLUDED') || B_PROLOG_INCLUDED!==true) die();

foreach ($arResult as &$item) {
    if (isset($item['LINK'])) {
        $item['LINK'] .= (strpos($item['LINK'], '?') === false ? '?' : '&') . 'from=menu';
    }
    if (!empty($item['ITEMS'])) {
        // рекурсивный обход подменю
        foreach ($item['ITEMS'] as &$sub) {
            $sub['LINK'] .= (strpos($sub['LINK'], '?') === false ? '?' : '&') . 'from=menu';
        }
        unset($sub);
    }
}
unset($item);

Результат: все ссылки меню будут содержать ?from=menu.

Пример 6: Динамическое добавление блока рекламы в список новостей

Вставить рекламный блок после каждого 3-го элемента. В result_modifier.php модифицируем структуру:

Пример
if (!defined('B_PROLOG_INCLUDED') || B_PROLOG_INCLUDED!==true) die();

$newItems = [];
$counter = 0;
foreach ($arResult['ITEMS'] as $item) {
    $newItems[] = $item;
    $counter++;
    if ($counter % 3 === 0) {
        $newItems[] = [
            'TYPE' => 'AD',
            'AD_CODE' => '<div class="ad-banner">Реклама</div>'
        ];
    }
}
$arResult['ITEMS'] = $newItems;

Шаблон проверяет $item['TYPE'] и выводит рекламу.

Все примеры показывают, как гибко можно адаптировать результат работы компонента под конкретные требования. Каждый случай требует понимания структуры $arResult и осторожности при изменении данных, используемых шаблоном.

Модификатор результата Bitrix - comments

En
Bitrix result modifier php (php)