Custom ORM D7 entity development for 1C-Bitrix

Custom ORM D7 entity development for 1C-Bitrix We have been working with 1C-Bitrix for over 10 years and see many projects suffer from database chaos: raw SQL queries in components, duplicated logic, and missing types. ORM D7 is the tool that turns chaos into order. A DataManager entity describes

Our competencies:

Frequently Asked Questions

Custom ORM D7 entity development for 1C-Bitrix

We have been working with 1C-Bitrix for over 10 years and see many projects suffer from database chaos: raw SQL queries in components, duplicated logic, and missing types. ORM D7 is the tool that turns chaos into order. A DataManager entity describes a table, its fields, and relationships, so instead of $DB->Query you write MyTable::getList([...]). With 10+ years of experience, we have implemented 50+ projects on ORM D7 — from small custom modules to complete replacement of legacy schemas in catalogs with millions of records.

Why ORM D7 beats raw SQL queries?

Direct SQL queries via $DB->Query are a thing of the past. They are hard to maintain, do not provide typed objects, and do not integrate with Bitrix cache. ORM D7 solves all these problems: code becomes readable, safe (escaping automatic), and development speed increases 2–3 times compared to raw SQL. Moreover, ORM entities work with tagged cache, which is invalidated only when data changes, not on a timer. This is especially important on high-load projects. In our experience, caching improvements alone reduce DB queries by up to 4 times, and page load times drop by 6 times.

Basic DataManager structure

A minimal entity looks like this:

namespace MyProject\Storage; use Bitrix\Main\ORM\Data\DataManager; use Bitrix\Main\ORM\Fields\IntegerField; use Bitrix\Main\ORM\Fields\StringField; use Bitrix\Main\ORM\Fields\DatetimeField; class OrderLogTable extends DataManager { public static function getTableName(): string { return 'my_order_log'; } public static function getMap(): array { return [ new IntegerField('ID', [ 'primary' => true, 'autocomplete' => true, ]), new IntegerField('ORDER_ID', [ 'required' => true, ]), new StringField('ACTION', [ 'required' => true, 'size' => 100, ]), new DatetimeField('CREATED_AT', [ 'default_value' => new \Bitrix\Main\Type\DateTime(), ]), ]; } } 

Register the class in the module's autoload map or via PSR-4 in composer.json inside /local/.

ORM field types

Field class DB type Special features
IntegerField INT primary, autocomplete, unsigned
StringField VARCHAR size — column length
TextField TEXT for long strings
FloatField FLOAT / DECIMAL
BooleanField TINYINT(1) / CHAR(1) values — pair (N/Y)
DateField DATE returns \Bitrix\Main\Type\Date
DatetimeField DATETIME returns \Bitrix\Main\Type\DateTime
EnumField VARCHAR values — allowed values
JsonField TEXT / JSON automatic serialization/deserialization

Field validation is configured via the validation parameter.

How to correctly describe relationships in DataManager?

One-to-many (Reference) is described via Reference with the target entity class and join condition. After declaring the relationship, you get joined table data in queries.

$result = OrderLogTable::getList([ 'select' => ['ID', 'ACTION', 'ORDER_.USER_ID', 'ORDER_.DATE_INSERT'], 'filter' => ['ORDER_ID' => 42], ]); 

Creating the table: migrations

The table is created by the createDbTable() method of the entity class. For schema changes, use DDL queries via Application::getConnection(). Bitrix has no built-in migration mechanism — for production projects, we use a custom migrator script.

ORM queries: basic operations

Select with conditions:

$result = OrderLogTable::getList([ 'select' => ['ID', 'ORDER_ID', 'ACTION', 'CREATED_AT'], 'filter' => [ '>CREATED_AT' => new \Bitrix\Main\Type\DateTime('-7 days'), 'ACTION' => 'STATUS_CHANGED', ], 'order' => ['CREATED_AT' => 'DESC'], 'limit' => 50, ]); while ($row = $result->fetch()) { } 

Add, update, delete are performed via add(), update(), delete(). ORM automatically checks types and escapes values.

Query caching

ORM integrates with Bitrix cache:

$result = OrderLogTable::getList([ 'select' => ['ID', 'ACTION'], 'filter' => ['ORDER_ID' => 42], 'cache' => ['ttl' => 3600, 'cache_joins' => true], ]); 

The cache is automatically invalidated when data changes through ORM methods (if managed cache is enabled).

Typical timelines and costs

Task Time Typical cost
1–3 simple entities (no relationships, CRUD operations) 2–4 days $500–$1000
Complex schema: 5–10 entities with relationships, validation, events 1–2 weeks $1500–$3000
Complete replacement of legacy tables with custom ORM schema and data migration 2–4 weeks $3000–$5000

Clients typically save 30–50% on annual maintenance after migrating to ORM. DataManager entities are an investment in code readability and maintainability. A year later, a developer who didn't write this code will understand the DB schema in an hour, not a day. Raw SQL offers no such guarantee.

What is included in the work

  • Database schema documentation — description of tables, fields, relationships, and indexes.
  • Entity source code — DataManager classes with validation and events.
  • Migration scripts — for creating and altering tables, data transfer.
  • Deployment instructions — module installation and update order.
  • 2 weeks of support after delivery — bug fixes, consultations.
Example of a typical migration
$conn = \Bitrix\Main\Application::getConnection(); $conn->query("ALTER TABLE my_order_log ADD COLUMN CONTEXT TEXT DEFAULT NULL"); $conn->query("CREATE INDEX idx_order_log_created ON my_order_log (CREATED_AT)"); 

Common mistakes when using ORM D7

  • Missing indexes — queries slow down on volumes over 100,000 rows.
  • Wrong field types — e.g., StringField instead of TextField, truncating data.
  • Ignoring caching — every query hits the DB even though data rarely changes.
  • Avoiding relationships — manually loading related records leads to N+1 queries.

We avoid these pitfalls through code review and load testing.

How we implement ORM entities: a real case from our practice

A project from our practice: an online store with a catalog of 300,000 products. Initially, all selections used CIBlockElement::GetList with many parameters. Performance dropped; pages loaded in 5 seconds. We developed an ORM layer for SKUs, discounts, and stock for this client. Result: page list generation time dropped to 0.8 seconds, and complex filters took 0.3 seconds. We also added tagged caching, reducing DB queries by 4 times. On average, maintenance budget savings for such projects are 30–50%.

Process of work

  1. Analytics — we study the current DB structure, business requirements, and load.
  2. Design — we draw the ORM entity schema, define relationships and indexes.
  3. Implementation — we write entity code, CRUD methods, events, and caching.
  4. Testing — unit tests, load testing with real volumes.
  5. Deployment — we apply migrations, update access, deliver documentation.

Contact us to discuss your project. We will assess complexity and offer timelines without obligations. Order a consultation today.

Read more about ORM on Wikipedia and official 1C-Bitrix documentation (dev.1c-bitrix.ru).