Extending the logic of standard components without copying 1C-Bitrix

Our company is engaged in the development, support and maintenance of Bitrix and Bitrix24 solutions of any complexity. From simple one-page sites to complex online stores, CRM systems with 1C and telephony integration. The experience of developers is confirmed by certificates from the vendor.
Our competencies:
Development stages
Latest works
  • image_website-b2b-advance_0.png
    B2B ADVANCE company website development
    1175
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Website development for FIXPER company
    811
  • image_bitrix-bitrix-24-1c_development_of_an_online_appointment_booking_widget_for_a_medical_center_594_0.webp
    Development based on Bitrix, Bitrix24, 1C for the company Development of an Online Appointment Booking Widget for a Medical Center
    564
  • image_bitrix-bitrix-24-1c_mirsanbel_458_0.webp
    Development based on 1C Enterprise for MIRSANBEL
    747
  • image_crm_dolbimby_434_0.webp
    Website development on CRM Bitrix24 for DOLBIMBY
    655
  • image_crm_technotorgcomplex_453_0.webp
    Development based on Bitrix24 for the company TECHNOTORGKOMPLEKS
    976

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)