Customizing Standard Components via result_modifier in 1C-Bitrix
result_modifier.php — a file inside the component template folder that executes after the component itself runs but before the template renders. At this point $arResult is already populated with data — you can add, modify, or remove any data before output. This is the primary mechanism for customizing standard components without touching the core.
How the mechanism works
Component execution sequence:
1. component.php (component logic, populates $arResult)
2. result_modifier.php (modifies $arResult within the template)
3. template.php (renders HTML)
4. component_epilog.php (post-processing after render)
result_modifier.php lives inside the template folder:
/local/templates/{site_template}/components/bitrix/catalog.element/default/result_modifier.php
Or inside a copied component template:
/local/components/bitrix/catalog.element/templates/default/result_modifier.php
If you created a component template through the administrative interface (Settings → Components → Component Templates), result_modifier.php is created in the template copy automatically.
What is available in result_modifier
The following are available in the file:
-
$arResult— the component result, an array of data -
$arParams— component parameters (from the template call or set automatically) -
$this— the component object (CBitrixComponent) - All global 1C-Bitrix objects:
$USER,$APPLICATION,$DB
Practical applications
Adding data from an external source to a product card:
In catalog.element, $arResult does not contain, for example, the average rating from a reviews table. Adding it:
// result_modifier.php for bitrix:catalog.element
\Bitrix\Main\Loader::includeModule('iblock');
$productId = $arResult['ID'];
// Fetch rating from a custom reviews table
$rating = ReviewTable::getAggregateByProduct($productId);
$arResult['AVERAGE_RATING'] = $rating['avg'] ?? 0;
$arResult['REVIEWS_COUNT'] = $rating['count'] ?? 0;
Now in template.php this data is available via $arResult['AVERAGE_RATING'].
Enhancing the product list (catalog.section): adding warehouse stock data:
// result_modifier.php for bitrix:catalog.section / bitrix:catalog.smart.filter
\Bitrix\Main\Loader::includeModule('catalog');
$productIds = array_column($arResult['ITEMS'], 'ID');
// Fetch stock levels for the primary warehouse
$stocks = \Bitrix\Catalog\StoreProductTable::getList([
'filter' => [
'PRODUCT_ID' => $productIds,
'STORE.ACTIVE' => 'Y',
'STORE.XML_ID' => 'MAIN_WAREHOUSE',
],
'select' => ['PRODUCT_ID', 'AMOUNT'],
])->fetchAll();
$stockMap = array_column($stocks, 'AMOUNT', 'PRODUCT_ID');
foreach ($arResult['ITEMS'] as &$item) {
$item['MAIN_STOCK'] = $stockMap[$item['ID']] ?? 0;
$item['IN_STOCK'] = $item['MAIN_STOCK'] > 0;
}
unset($item);
Substituting price based on user group:
// result_modifier.php for bitrix:catalog.element
global $USER;
$userGroups = $USER->GetUserGroupArray();
$isWholesale = in_array(WHOLESALE_GROUP_ID, $userGroups);
if ($isWholesale && isset($arResult['CATALOG_PRICE_2'])) {
// Show wholesale price instead of retail
$arResult['DISPLAY_PRICE'] = $arResult['CATALOG_PRICE_2'];
} else {
$arResult['DISPLAY_PRICE'] = $arResult['CATALOG_PRICE_1'] ?? 0;
}
Building breadcrumbs with custom data:
// Adding SEO breadcrumbs from the section
if (!empty($arResult['SECTION'])) {
$arResult['SEO_BREADCRUMBS'] = buildSeoJsonLd(
$arResult['SECTION']['PATH'],
$arResult['NAME']
);
}
Performance: cache inside result_modifier
result_modifier.php executes on every component render. If it contains heavy queries — wrap them in a cache:
// result_modifier.php
$cacheId = 'product_extra_' . $arResult['ID'];
$cacheDir = '/product_extra/';
$ttl = 3600;
$cache = \Bitrix\Main\Data\Cache::createInstance();
if ($cache->initCache($ttl, $cacheId, $cacheDir)) {
$extraData = $cache->getVars();
} elseif ($cache->startDataCache()) {
$extraData = fetchHeavyProductData($arResult['ID']); // your slow query
$cache->endDataCache($extraData);
}
$arResult['EXTRA'] = $extraData;
Important: if the component itself uses cache (most catalog components do), then result_modifier.php does not execute when the response is served from cache — only template.php runs with the cached $arResult. Keep this in mind: data added to $arResult inside result_modifier.php is cached together with $arResult.
Limitations of result_modifier
result_modifier.php is not the place for side-effect operations: creating database records, sending notifications, or changing state. It is for reading and transforming $arResult only. For side effects after rendering — use component_epilog.php.
You cannot modify SQL queries already executed by the component — you can only augment the result with new queries. If you need to change data retrieval logic, you will need to override the component itself via /local/components/.
Debugging
If result_modifier.php is not being applied — check:
- The file path: it must be in the template folder used in the component call
- The component is not being served entirely from cache (in that case
result_modifier.phpis not called for cached data — onlytemplate.phpruns) - No syntax errors in the file — execution errors are silently ignored in production mode
// At the top of result_modifier.php for debugging
// Verify that the file is actually being executed:
if (defined('LOG_RESULT_MODIFIER')) {
file_put_contents('/tmp/rm_debug.log', date('H:i:s') . ' ' . __FILE__ . "\n", FILE_APPEND);
}
Scope of work: analyzing the $arResult structure of the target component, writing result_modifier.php, adding cache coverage — typically 4–16 hours depending on the complexity of the data source.







