Bitrix Component Development
result_modifier.php — The Most Underrated File in Bitrix
Let's start with something most Bitrix developers discover after a year of work. result_modifier.php sits in the component template folder and executes between the component logic and the rendering of template.php. It receives the populated $arResult and can supplement, regroup, or enrich it — all while remaining untouched when the component itself is updated.
Real tasks we solve through it constantly:
- The
bitrix:catalog.sectioncomponent doesn't select trade offer properties — we fetch them viaCIBlockElement::GetListright in the modifier and store them in$arResult['OFFERS_PROPS'] - Grouping elements by sections or arbitrary properties — the standard component returns a flat array, but the design requires tabs by category
- Calculating discounts, ratings, delivery times — everything that depends on business logic absent from the standard component
- Preparing JSON arrays for JavaScript —
$arResult['JS_DATA'] = json_encode(...)right in the modifier, and in the template it's just<script>var data = <?=$arResult['JS_DATA']?></script>
The key rule: heavy database queries in result_modifier are acceptable because it operates within the caching zone. But in component_epilog.php — they're not.
component_epilog.php — Outside the Cache, and That's Its Power
Executes after the template is rendered and outside the caching zone. On every hit, even cached ones. This is where we put:
- Authorization checks and personalized elements: "Add to Favorites," "Buy in 1 Click"
- Setting meta tags and titles via
$APPLICATION->SetTitle() - Including JS/CSS via
Asset::getInstance()->addJs() - Breadcrumb navigation
Critical: no heavy SQL here. CIBlockElement::GetList in the epilog is a direct path to degradation because the query will execute on every page view, bypassing the cache.
Component Architecture
A component is a self-contained module:
-
class.php — an OOP class extending
CBitrixComponent. Business logic, data fetching, parameter validation. In new components, we use only this;component.phpis a procedural relic. -
template.php — pure HTML +
$arResult. No business logic. - .parameters.php — input parameter definitions for the admin panel.
- .description.php — metadata: name, category, icon.
Everything on the D7 core, ORM classes, and the event model. CIBlockElement::GetList — only when D7 ORM doesn't cover the case.
Custom Components
Standard components cover typical scenarios. But non-standard business logic shows up on every other project:
- Cost calculators with multi-parameter formulas
- Integration components for external APIs (CRM, ERP, logistics)
- Multi-step product configurators and booking systems
- Admin panel dashboards
The principle: a component is reusable — parameterization instead of hardcoding. We document parameters and behavior so that six months later no one has to reverse-engineer their own code.
Ajax: D7 Controllers
The built-in ajax mode of catalog components (AJAX_MODE = Y) covers the basics — pagination, filters, sorting without full page reloads.
For custom logic — Bitrix\Main\Engine\Controller controllers. Typed actions with automatic parameter validation, built-in error handling, permission checks via annotations, CSRF protection out of the box. Endpoints through ajax.php or custom routing. Response in JSON. Lazy loading of catalog on scroll, inline editing — all through controllers.
Caching — Defines Site Speed
The difference between 200 ms and 3 seconds is the caching strategy.
Managed cache — auto-invalidation on data changes. Added a product to an infoblock — cache is rebuilt. The most reliable option for content components. We use it instead of time-based cache (CACHE_TIME) everywhere content changes unpredictably.
Separation by user groups: guest / authenticated / admin see different content — different cache. Personal data — strictly in component_epilog, outside the cache.
Tagged cache for invalidating related data — a product changed, the catalog cache and related recommendations cache are both cleared.
Composite site: the static part is served as HTML, dynamic zones are loaded via ajax requests. TTFB < 100 ms. But it requires careful markup of dynamic zones in templates — otherwise someone else's cart gets cached.
Cache hit ratio monitoring: if cache misses exceed 30% — the configuration is broken.
Development Timelines
| Task Type | Timeline |
|---|---|
| Custom template for a standard component | 2–8 hours |
| result_modifier with additional logic | 2–4 hours |
| Simple custom component | 1–3 days |
| Complex component with ajax and caching | 3–7 days |
| Integration component (external API) | 3–10 days |
Every component comes with documentation: parameter descriptions, data format, usage examples. So that six months later the next developer doesn't have to guess what's going on.







