d7-orm-queries
Skill4 starsChanged 4 months agoMITView on GitHub
Query 1C-Bitrix data with the D7 ORM DataManager API — getList with filter/select/order/runtime, references and joins, transactions, fetch vs fetchAll — and choose it over the legacy CIBlockElement API for new code. Use when the user is fetching rows through the D7 ORM — a DataManager `getList()` with `filter`, `select`, `order`, or `limit` — or building a new query over a Bitrix entity or highload block; the user is deciding between the D7 ORM (`ElementTable::getList`) and the legacy `CIBlockElement::GetList`, or migrating a query from the legacy API to the ORM; the user needs to join related entities in the ORM — a declared `Reference` field, a `runtime` field with `ReferenceField`/`ExpressionField`, or selecting columns across the join; the user needs several ORM `add`/`update`/`delete` operations to succeed or fail atomically — starting a DB transaction and committing or rolling back around DataManager writes. Keywords: bitrix, orm, datamanager, getlist, query, filter, runtime, reference, transaction,…
In this repository119 other files it gives its agents.
- strategy-bitrix-d7-incrementalSkill
- strategy-explicit-over-magicSkill
- strategy-framework-conventionsSkill
- strategy-legacy-safe-editsSkill
- strategy-minimal-diffSkill
- strategy-no-untested-refactorSkill
- strategy-preserve-public-apiSkill
- strategy-readonly-immutabilitySkill
- strategy-security-firstSkill
- strategy-test-drivenSkill
- openapiSkill
- paginationSkill
- rest-designSkill
- versioningSkill
- cqrsSkill
- dependency-inversionSkill
- hexagonalSkill
- repository-patternSkill
- service-layerSkill
- bitrixSkill
- findings-reportSkill
- laravelSkill
- performanceSkill
- phpSkill
- project-analysisSkill
- refactoringSkill
- roi-prioritizationSkill
- securitySkill
- stack-detectionSkill
- cache-layersSkill
- component-engineSkill
- d7-eventsSkill
- d7-loaderSkill
- iblock-elementsSkill
- kernel-main-moduleSkill
- localizationSkill
- controllersSkill
- ormSkill
- validationSkill
- controllersSkill
- query-builderSkill
- routingSkill
- autoload-psr4Skill
- dependency-managementSkill
- scriptsSkill
- connection-poolingSkill
- mysql-indexingSkill
- redisSkill
- transactionsSkill
- ci-github-actionsSkill
- deployment-strategiesSkill
- docker-phpSkill
- env-configSkill
- associationsSkill
- entities-mappingSkill
- orm-basicsSkill
- entity-apiSkill
- module-developmentSkill
- routing-controllersSkill
- formsSkill
- The first 60 shown.
The file
We split this skill into the rules it gives an agent
12 rules. About 0 are common, 6 are distinctive, and 6 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈6
Distinctive · estimate
This author's own choices. Worth a look.
6
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 90
Do not use this skill for a non-Bitrix ORM (Doctrine, Eloquent, plain PDO).
- Distinctive: line 92
Do not use this skill for raw DDL, migrations, or schema surgery (CREATE/ALTER TABLE).
- Very specific: line 139
Verify: The module is loaded, so ElementTable resolves without a class-not-found.
- Very specific: line 140
Call EntityTable::getList with an explicit select, an order, and a filter whose keys are code literals and whose values hold the validated …
- Distinctive: line 142
Verify: The filter binds input as a parameter and selects only the named columns.
- Distinctive: line 145
Verify: Rows are read once.
- Distinctive: line 161
Verify: A transaction is open on the current connection.
- Very specific: line 162
Run the add/update/delete calls, checking isSuccess() on each Result and throwing when one fails so control reaches the catch block.
- Distinctive: line 164
Verify: A failed write raises before any commit runs.
- Very specific: line 165
Call $conn->commitTransaction().
- Distinctive: line 167
Verify: A mid-sequence failure leaves the database in its pre-transaction state.
- Very specific: line 180
When several ORM writes must be atomic, get the connection with Application::getConnection(), call startTransaction(), and commit on …
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
<!-- generated: do not edit — source: knowledge/atoms/bitrix/d7-orm-queries/ --><!-- schema: atomVersion=3 skillVersion=2 graphVersion=1 --><!-- compile: @php-skills/compiler v1.0.0 (deterministic build) -->Bitrix D7 ORM Queries
When to use
- The user is fetching rows through the D7 ORM — a DataManager
getList()withfilter,select,order, orlimit— or building a new query over a Bitrix entity or highload block. - The user is deciding between the D7 ORM (
ElementTable::getList) and the legacyCIBlockElement::GetList, or migrating a query from the legacy API to the ORM. - The user needs to join related entities in the ORM — a declared
Referencefield, aruntimefield withReferenceField/ExpressionField, or selecting columns across the join. - The user needs several ORM
add/update/deleteoperations to succeed or fail atomically — starting a DB transaction and committing or rolling back around DataManager writes.
When NOT to use
- Do not use this skill for a non-Bitrix ORM (Doctrine, Eloquent, plain PDO); the
DataManager
getList/filter syntax and\Bitrix\Main\ORMare Bitrix-specific. - Do not use this skill for raw DDL, migrations, or schema surgery (CREATE/ALTER TABLE); it covers reading and writing rows through the ORM, not managing the underlying database schema.
Core guidance
- Keep filter keys static; put user input only in values — Build
filterkeys (field names and operators like'>=DATE_CREATE') from literals in your code, and place untrusted input only in filter values, which the ORM parameterises. - Prefer the D7 ORM over legacy element queries for new code — In new code query through the D7 ORM (
ElementTable::getList/ a DataManager) rather thanCIBlockElement::GetList; keep the legacy call only when patching existing legacy code where a rewrite is out of scope.
Decision Engine
Walk this section before choosing an implementation path.
Are you writing a new query, or editing an existing legacy CIBlockElement query?
├── Editing legacy query: → legacy — keep CIBlockElement::GetList and make a minimal local edit; do not rewrite to the ORM opportunistically.
└── New query: Does the query need to join a related entity that has no declared Reference field?
├── No ad hoc join needed: → orm — use DataManager getList with explicit select/filter/order and declared Reference fields for joins.
└── Ad hoc join needed: → runtime — declare a runtime ReferenceField (or ExpressionField) inside this getList to express the one-off join.
Full engine: references/decision-engine.md — Decision Tree, Priority Matrix, Decision Table.
Workflows
Workflow: Safe Getlist
Progress:
- [ ] Guard the query with `if (!\Bitrix\Main\Loader::includeModule('iblock')) { ..
- [ ] Call `EntityTable::getList` with an explicit `select`, an `order`, and a `filter`
whose keys are code literals and whose values hold the validated input
- [ ] Iterate a large result with `while ($row = $result->fetch())`, or call
`$result->fetchAll()` for a small bounded set- Guard the query with
if (!\Bitrix\Main\Loader::includeModule('iblock')) { ... }before referencing the entity's DataManager.- Verify: The module is loaded, so
ElementTableresolves without a class-not-found.
- Verify: The module is loaded, so
- Call
EntityTable::getListwith an explicitselect, anorder, and afilterwhose keys are code literals and whose values hold the validated input.- Verify: The filter binds input as a parameter and selects only the named columns.
- Iterate a large result with
while ($row = $result->fetch()), or call$result->fetchAll()for a small bounded set.- Verify: Rows are read once; memory stays flat for large sets.
Workflow: Atomic Writes
Progress:
- [ ] Get the connection with `$conn = \Bitrix\Main\Application::getConnection();` and
call `$conn->startTransaction();` before the first write
- [ ] Run the `add`/`update`/`delete` calls, checking `isSuccess()` on each Result and
throwing when one fails so control reaches the catch block
- [ ] Call `$conn->commitTransaction();` after the last successful write, and
`$conn->rollbackTransaction();` in the catch block on any failure- Get the connection with
$conn = \Bitrix\Main\Application::getConnection();and call$conn->startTransaction();before the first write.- Verify: A transaction is open on the current connection.
- Run the
add/update/deletecalls, checkingisSuccess()on each Result and throwing when one fails so control reaches the catch block.- Verify: A failed write raises before any commit runs.
- Call
$conn->commitTransaction();after the last successful write, and$conn->rollbackTransaction();in the catch block on any failure.- Verify: A mid-sequence failure leaves the database in its pre-transaction state.
Best practices
- Pass an explicit
selectlisting only the fields you need, including any reference sub-fields (['ID', 'NAME', 'SECTION.NAME']); do not rely on the default select or fetch every column - Loop with
while ($row = $result->fetch())for large or streamed result sets; call$result->fetchAll()only for small, bounded sets you need entirely in memory at once - Model a relation you use repeatedly as a declared
Referencefield on the entity map; express a one-off join inside a single query with aruntimeReferenceFieldinstead of adding permanent schema - When several ORM writes must be atomic, get the connection with
Application::getConnection(), callstartTransaction(), and commit on success orrollbackTransaction()inside a catch on failure
Anti-patterns
| Anti-pattern | Why it fails | Do instead |
| Ignoring the write result object | A failed write returns a Result with errors rather than throwing, so ignoring it<br>silently drops data and hides validation failures. | Check $result->isSuccess() and read $result->getErrorMessages(), handling or<br>surfacing failures before proceeding. |
| Interpolating user input into a filter key | Only filter values are parameterised, so an interpolated key is a direct SQL injection vector. | Whitelist the allowed field to a fixed set of literals and keep the untrusted<br>input in the filter value only. |
| Relying on the default select to load every column | Loading unused columns wastes IO and memory and makes the query's real<br>dependencies invisible. | Pass an explicit select naming exactly the fields and reference sub-fields the<br>code uses. |
See references/anti-patterns.md.
Debugging
- Stale reads from the ORM entity cache after a direct write — The ORM caches results and only invalidates them on writes through its own DataManager. Fix: Route writes through the entity's DataManager so its cache is cleared, or clean the entity's managed cache explicitly after an out-of-band change.
- fetchAll on an unbounded result exhausts memory — fetchAll materialises the whole result set at once instead of streaming it. Fix: Iterate with
while ($row = $result->fetch())for large sets, and always bound paged queries withlimit/offset. - N+1 queries by fetching relations in a loop — Each loop iteration round-trips to the database instead of joining once. Fix: Select the related columns through a Reference or runtime ReferenceField in the original getList so the relation loads in a single joined query.
Performance notes
- fetchAll on an unbounded result exhausts memory — Calling
fetchAll()on a query with nolimitloads every matching row into an array and can exhaust PHP memory on a large table. - N+1 queries by fetching relations in a loop — Fetching a list and then calling a second getList per row to load its related entity issues one query per row (N+1) and dominates response time.
Production checklist
- Every getList passes an explicit
selectnaming only the required fields and reference sub-fields, not a bare or default select. - Large or unbounded results are streamed with
fetch(); only small bounded sets usefetchAll(), and paged queries setlimit/offset. - No untrusted input is interpolated into a filter key, field name, or operator; input flows only into parameterised filter values.
- The module that owns the DataManager (for example
iblock) is included with a checkedLoader::includeModulebefore any of its ORM classes are referenced. - The result of every
add/update/deleteis checked withisSuccess()and errors are read fromgetErrorMessages()rather than assumed to have worked. - Any group of ORM writes that must be atomic runs inside a connection transaction with a commit on success and a rollback in the catch path.
Version notes
- The D7 ORM supersedes CIBlockElement for new reads — The
\Bitrix\Main\ORMDataManager API (getList with entityQuery, references, and runtime fields) matured on the D7 platform (main ~16+); the legacyCIBlockElement::GetListremains for older code and iblock-specific properties.
Security
- Never interpolate untrusted input into filter or order keys — Treat every value from the request as untrusted: never build a
filterkey, field name, operator,ordercolumn, orselectalias from user input. The ORM parameterises filter values only; keys are compiled into SQL literally.
Testing
- Activation and decision behavior are verified in
eval/scenarios/bitrix/d7-orm-queries.yaml.
Additional resources
- references/anti-patterns.md
- references/core.md
- references/decision-engine.md
- references/examples.md
- references/pitfalls.md
- references/recipes.md
- references/version-differences.md
Knowledge graph
Prerequisites: bitrix/d7-loader
Related: ai-rules/strategy-bitrix-d7-incremental, bitrix/cache-layers
Behavior constraints: ai-rules/strategy-legacy-safe-edits
<!-- graph-fragment: graph/fragments/bitrix/d7-orm-queries.yaml -->Skill contract
Produces: orm.d7-query
Requires: bitrix.module-loaded
Risk: medium · Confidence: 0.9
<!-- contract-fragment: contracts/fragments/bitrix/d7-orm-queries.yaml -->Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

