Refactoring the old API to D7 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

Refactoring Legacy API to D7 in 1C-Bitrix

Bitrix projects that are 5–10 years old are written using the "old API": the global $APPLICATION, CIBlockElement::GetList(), $DB->Query() with string concatenation, functions from CModule. This code works — for now. Problems arise when trying to scale, test, or hand it over to another team. Refactoring to D7 is not "rewriting for the sake of rewriting" — it is about reducing the cost of maintenance and eliminating a class of errors typical of procedural code.

What Exactly Changes When Moving to D7

Global functions → DataManager methods:

// Before — old API
$res = CIBlockElement::GetList(
    ['SORT' => 'ASC'],
    ['IBLOCK_ID' => 5, 'ACTIVE' => 'Y'],
    false,
    ['nPageSize' => 20],
    ['ID', 'NAME', 'PROPERTY_PRICE']
);
while ($arItem = $res->GetNextElement()) {
    $arFields = $arItem->GetFields();
    $arProps  = $arItem->GetProperties();
}

// After — D7 ORM
$result = \Bitrix\Iblock\Elements\ElementProductsTable::getList([
    'select' => ['ID', 'NAME', 'PROPERTY_PRICE_VALUE'],
    'filter' => ['=IBLOCK_ID' => 5, '=ACTIVE' => 'Y'],
    'order'  => ['SORT' => 'ASC'],
    'limit'  => 20,
]);
foreach ($result->fetchCollection() as $element) {
    $name  = $element->getName();
    $price = $element->get('PROPERTY_PRICE_VALUE');
}

SQL strings → Connection API:

// Before — direct SQL with concatenation
global $DB;
$id = intval($id); // the entire "protection"
$res = $DB->Query("SELECT * FROM b_custom_table WHERE ID = " . $id);

// After — parameterized query
$connection = \Bitrix\Main\Application::getConnection();
$result = $connection->query(
    "SELECT * FROM my_custom_table WHERE ID = ?",
    [$id]
);
// or via SqlHelper for complex cases
$helper = $connection->getSqlHelper();
$safeId = $helper->forSql((string)$id);

Global $APPLICATION → Context and Application:

// Before
global $APPLICATION;
$APPLICATION->SetTitle('My Page');
$APPLICATION->IncludeFile('/local/include/header.php');

// After — within component or controller context
$this->arResult['PAGE_TITLE'] = 'My Page';
// Inclusion via D7 mechanisms or Inertia/React approach

Refactoring Strategy: Incremental Phases

Rewriting the entire project at once is a bad idea. Even a small online store contains 50,000+ lines of code. The correct approach is layered refactoring with prioritization.

Phase 1. Audit. Inventory of problem areas:

  • SQL queries with string concatenation (potential SQL injections)
  • Global variables in logic (global $DB, global $USER, global $APPLICATION)
  • Heavy queries via CIBlockElement::GetList without limits
  • Duplicated logic code across different files

Phase 2. Create a service layer. New D7 API service classes for each area of responsibility. Old code calls the new services.

Phase 3. Gradual replacement. Module by module and functional block by block. After each block is replaced — regression testing.

Phase 4. Remove obsolete code. Only after the new path has been battle-tested.

Infoblock Transition to D7 Classes

Starting from Bitrix version 20.x, auto-generated classes for infoblocks are available via Bitrix\Iblock\Elements:

// Enable in the infoblock settings — "API code" flag
// A class is generated with namespace Bitrix\Iblock\Elements

$result = \Bitrix\Iblock\Elements\ElementCatalogTable::getList([
    'select' => ['ID', 'NAME', 'IBLOCK_SECTION_ID'],
    'filter' => ['=ACTIVE' => 'Y'],
]);

This is not a full replacement for CIBlockElement — for write operations (add/update), the old API is still often used, as D7 wrappers for infoblock writes do not cover all scenarios.

Typical Pitfalls During Refactoring

Losing events. If the old code called CIBlockElement::Update(), event handlers were attached to it (OnAfterIBlockElementUpdate). When switching to D7 methods, the ORM events are different. You need to check which handlers are bound to the old events.

Differences between GetList and getList results. CIBlockElement::GetList returns infoblock properties in a specific format with _VALUE, _ENUM_ID suffixes. ORM methods return a different structure. Code that processes results needs to be adapted.

Transactions. The old API did not always wrap operations in transactions. D7 provides explicit transactions:

$connection = \Bitrix\Main\Application::getConnection();
$connection->startTransaction();
try {
    // multiple operations
    $connection->commitTransaction();
} catch (\Throwable $e) {
    $connection->rollbackTransaction();
    throw $e;
}

What to Refactor First

Priority What Why
Critical SQL with string concatenation Security
High Heavy GetList without limits Performance
Medium Business logic in components Maintainability
Planned Global variables for styling Readability

Timeline

Project Scale Refactoring Timeline
Small site (up to 30 files with logic) 2–4 weeks
Medium online store (50–150 files) 2–3 months
Large portal or marketplace 4–8 months (incremental)

Refactoring is technical debt you pay now to avoid paying three times as much in a year. Code on the old API does not become more reliable over time — with each Bitrix update, the risk of incompatibility grows.