Extending Standard Component Logic Without Copying in 1C-Bitrix
Copying a component from /bitrix/components/ to /local/components/ is a workable but costly approach. The copy stops receiving core updates: bug fixes, support for new APIs, compatibility with new editions. After a year or two the copy diverges from the original to the point where merging becomes impossible. 1C-Bitrix provides several mechanisms for extending component logic without a full copy.
Extending Standard Component Logic Without Copying in 1C-Bitrix
Mechanism 1: result_modifier.php
The simplest approach. The result_modifier.php file in the component template folder executes after the main component logic but before the template. $arResult is available by reference — data can be added, modified, or removed.
/local/templates/my_site/components/bitrix/catalog.section/.default/result_modifier.php
<?php
if (!defined('B_PROLOG_INCLUDED') || B_PROLOG_INCLUDED !== true) die();
// Add the average rating from a custom table to each product
$ids = array_column($arResult['ITEMS'], 'ID');
if ($ids) {
$ratings = MyRatingService::getAverageForItems($ids);
foreach ($arResult['ITEMS'] as &$item) {
$item['MY_RATING'] = $ratings[$item['ID']] ?? 0;
}
}
Limitation: result_modifier.php only works with the result — it cannot change the database query parameters inside the component.
Mechanism 2: Events (component events)
Most standard components fire events via \Bitrix\Main\EventManager. This is the most powerful mechanism — it allows you to intervene in logic at the level of query construction, filtering, and result preparation.
To find events for a specific component, search its source code:
grep -r "GetModuleEvents\|AddEventHandler\|fireEvent\|sendEvent" \
/bitrix/components/bitrix/catalog.section/
Example: the OnBeforeIBlockElementGetList event in the iblock module:
// In /local/php_interface/init.php
\Bitrix\Main\EventManager::getInstance()->addEventHandler(
'iblock',
'OnBeforeIBlockElementGetList',
function (\Bitrix\Main\Event $event) {
$filter = $event->getParameter('filter');
// Add hiding of products without a price
$filter['!CATALOG_PRICE_1'] = false;
$event->setParameter('filter', $filter);
return $event;
}
);
Mechanism 3: Component Extensions
Introduced in 1C-Bitrix with D7. Allows creating an extension class that inherits or decorates the standard component's logic without copying it.
Create the component class file in /local/:
/local/components/bitrix/catalog.section/class.php
<?php
if (!defined('B_PROLOG_INCLUDED') || B_PROLOG_INCLUDED !== true) die();
// Include the original class
\Bitrix\Main\Loader::includeModule('iblock');
\Bitrix\Main\Loader::includeModule('catalog');
// Inherit the original component
class MyCatalogSectionComponent extends \Bitrix\Iblock\Component\ElementList
{
protected function getFilter(): array
{
$filter = parent::getFilter();
// Extend filter: hide products without an image
$filter['!PREVIEW_PICTURE'] = false;
return $filter;
}
protected function prepareElementData(array $element): array
{
$element = parent::prepareElementData($element);
// Add a computed field
$element['IS_NEW'] = (time() - strtotime($element['DATE_CREATE'])) < 86400 * 30;
return $element;
}
}
1C-Bitrix automatically finds /local/components/bitrix/catalog.section/class.php and uses it instead of the original — while templates from /bitrix/ continue to work.
Mechanism 4: Aggregation via a wrapper
When you need to completely override logic but retain the ability to call the original, create a wrapper component with a different name:
/local/components/myproject/catalog.section.extended/component.php
<?php
// Call the original component from inside your own
$this->includeComponentTemplate();
// Or:
$APPLICATION->IncludeComponent(
'bitrix:catalog.section',
'.default',
$arParams,
$this
);
Mechanism 5: Overriding methods via /local/php_interface/init.php
For cases where a component uses global functions or static methods:
// init.php — executes on every request
// Intercept the SEO data preparation method
\Bitrix\Main\EventManager::getInstance()->addEventHandler(
'main',
'OnPageStart',
function () {
// Initialize custom logic
}
);
Choosing an approach: comparison
| Task | Mechanism |
|---|---|
| Add a computed field to the result | result_modifier.php |
| Modify the component's SQL filter | OnBefore* event |
| Extend business logic, override methods | Extension class in /local/ |
| Full logic replacement while keeping templates | Extension class + method overrides |
| Reuse a component with different parameters | Wrapper component |
Practical example: extending bitrix:catalog.section
Task: add the number of active reviews to the standard product list without copying the component.
// /local/components/bitrix/catalog.section/class.php
class MyCatalogSectionComponent extends \Bitrix\Iblock\Component\ElementList
{
public function executeComponent(): void
{
parent::executeComponent();
// After the parent executes — enrich data
$this->enrichWithReviewCounts();
}
private function enrichWithReviewCounts(): void
{
if (empty($this->arResult['ITEMS'])) return;
$ids = array_keys($this->arResult['ITEMS']);
$counts = \Bitrix\Main\Application::getConnection()->query(
'SELECT ELEMENT_ID, COUNT(*) as CNT FROM b_iblock_element_prop_s' . REVIEWS_IBLOCK_ID .
' WHERE ELEMENT_ID IN (' . implode(',', array_map('intval', $ids)) . ')' .
' AND PROPERTY_' . REVIEW_STATUS_PROP . " = 'approved'" .
' GROUP BY ELEMENT_ID'
)->fetchAll();
$countMap = array_column($counts, 'CNT', 'ELEMENT_ID');
foreach ($this->arResult['ITEMS'] as &$item) {
$item['REVIEW_COUNT'] = (int)($countMap[$item['ID']] ?? 0);
}
}
}
The original component continues to receive core updates; custom logic stays in /local/.
Timelines
| Task | Timeline |
|---|---|
| Extending a single component via result_modifier | 2–4 hours |
| Extension via an inheriting class with method overrides | 1–3 days |
| Refactoring: replacing copied components with extensions | 3–8 days (depends on count) |







