PHPackages                             jiscariot/iblock-element-reader - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Database &amp; ORM](/categories/database)
4. /
5. jiscariot/iblock-element-reader

ActiveLibrary[Database &amp; ORM](/categories/database)

jiscariot/iblock-element-reader
===============================

Bitrix iblock element reader: load fields and properties from DB with batch resolve pipeline

25PHP

Since Jul 10Pushed 1mo agoCompare

[ Source](https://github.com/JIscariot/iblock-element-reader)[ Packagist](https://packagist.org/packages/jiscariot/iblock-element-reader)[ RSS](/packages/jiscariot-iblock-element-reader/feed)WikiDiscussions master Synced 1w ago

READMEChangelogDependenciesVersions (1)Used By (0)

jiscariot/iblock-element-reader
===============================

[](#jiscariotiblock-element-reader)

Bitrix iblock element reader: загрузка полей и свойств из БД и batch-резолв в типизированные значения.

Установка
---------

[](#установка)

```
composer require jiscariot/iblock-element-reader
```

**Требования:** PHP 8.1+, Bitrix CMS с модулем `iblock` (классы `Bitrix\Iblock\*`, `Bitrix\Main\*`).

```
IblockElementReader → IblockElementFetcher → ElementLoader → ResolvePipeline → IblockElementCollection
                                                      ↓
                                               Repository/*

```

Класс immutable: каждый `filter` / `withFields` / `withProps` / `withResolvers` возвращает клон.

Reader **не ходит в `b_iblock`**: scope — через `filter(iblock: ...)` или `IBLOCK_ID` из загруженных элементов; storage mode — `VERSION` из `b_iblock_property`.

---

Быстрый старт
-------------

[](#быстрый-старт)

```
use IblockElementReader\IblockElementReader;

$elements = (new IblockElementReader())
    ->filter(id: [10, 20, 30], iblock: [5])
    ->withFields(['ID', 'NAME', 'PREVIEW_PICTURE.VALUE'])
    ->withProps(['MAKE_ID.VALUE', 'PRICE.VALUE', 'VIN'])
    ->get();

foreach ($elements->elements as $element) {
    $element->id;                              // int
    $element->name;                            // string|null
    $element->properties['MAKE_ID']->value->raw;       // сырое из БД
    $element->properties['MAKE_ID']->value->resolved;  // после .VALUE или null
}

$elements->elements[0] ?? null; // первый элемент
$elements->elements;            // IblockElement[]

// один элемент
$element = (new IblockElementReader())
    ->filter(id: 10, iblock: 5)
    ->withFields(['*'])
    ->first();
```

### filter — scope элементов и инфоблоков

[](#filter--scope-элементов-и-инфоблоков)

```
// id + iblock
->filter(id: [215674], iblock: [33])

// только id — iblock из IBLOCK_ID в row
->filter(id: [215674, 218572])

// все элементы iblock
->filter(iblock: [33])

// несколько iblock
->filter(id: [100, 200], iblock: [5, 10])

// ошибка: scope не определён
(new IblockElementReader())->get();
```

`iblock``id`Поведение`[5]``[10, 20]`элементы iblock 5 с указанными id`[5, 10]``[1, 2, 3]`элементы из iblock 5 и 10 по id`[5, 10]``[]`все элементы iblock 5 и 10не задан`[10, 20]`загрузка по id, iblock из row (один или несколько)не задан`[]`exception`filter(id: [])` без `iblock` — ошибка.

Повторный `filter()` меняет только переданные аргументы:

```
->filter(id: [10, 20])
->filter(iblock: [5])   // id остаётся [10, 20]
```

### Несколько инфоблоков

[](#несколько-инфоблоков)

```
$elements = (new IblockElementReader())
    ->filter(id: [100, 200, 300], iblock: [5, 10])
    ->withProps(['NAME.VALUE', 'ADDRESS.VALUE'])  // код должен существовать в каждом iblock
    ->get();

foreach ($elements->elements as $element) {
    $element->iblockId;              // 5 или 10
}
```

---

Главное правило: суффикс `.VALUE`
---------------------------------

[](#главное-правило-суффикс-value)

Без `.VALUE`С `.VALUE`**withFields**сырое значение (id файла, строка)`File` для `PREVIEW_PICTURE` / `DETAIL_PICTURE`**withProps**сырое значение из `prop_s`авто-резолв по типу свойстваПримеры:

```
// id файла
->withFields(['PREVIEW_PICTURE'])

// объект File { id, subdir, filename, src, size, width, height }
->withFields(['PREVIEW_PICTURE.VALUE'])

// id связанного элемента
->withProps(['STORE'])

// IblockElement связанного элемента (поля из withFields родительского запроса)
->withProps(['STORE.VALUE'])

// id enum-значения
->withProps(['STATUS'])

// Enumerate { id, value, xmlId }
->withProps(['STATUS.VALUE'])
```

---

withFields
----------

[](#withfields)

```
// withoutFields — в SELECT только ID (withFields не вызывался)
(new IblockElementReader())->filter(id: [10])->withProps([...])->get();

// все поля по умолчанию
->withFields(['*'])

// явный список
->withFields(['ID', 'NAME', 'PREVIEW_PICTURE.VALUE'])
```

ВызовSELECTбез `withFields``ID` (+ `IBLOCK_ID` служебно)`withFields(['*'])`ID, NAME, CODE, ACTIVE, SORT, PREVIEW\_PICTURE, DETAIL\_PICTURE, PREVIEW\_TEXT, DETAIL\_TEXT`withFields([])`только `ID`явный списокуказанные поля + `ID``ID` добавляется в SELECT автоматически, если не указан явно.

---

withProps
---------

[](#withprops)

```
// свойства не загружаются ($props = null)
(new IblockElementReader())->filter(iblock: [5])->get();

// все свойства с авто-резолвом (.VALUE для каждого кода)
->withProps([])
->withProps(['*.VALUE'])

// все свойства как есть (сырые строки / id)
->withProps(['*'])

// явный список
->withProps(['MAKE_ID.VALUE', 'PRICE', 'PHOTOS.VALUE'])
```

ВызовЧто происходитбез `withProps`свойства не читаются из БД`withProps([])`все коды, каждый с `.VALUE``withProps(['*'])`все коды, без резолва`withProps(['*.VALUE'])`все коды с резолвом + можно добавить явные пути---

Авто-резолв свойств (без кастомных резолверов)
----------------------------------------------

[](#авто-резолв-свойств-без-кастомных-резолверов)

При `.VALUE` pipeline выбирает стратегию в таком порядке:

1. **USER\_TYPE resolver** — встроенный (`Date`, `DateTime`) или из `withResolvers`
2. **Nested E** — если путь вида `STORE.VALUE.ADDRESS.VALUE`
3. **Тип свойства (PROPERTY\_TYPE)**:
    - `E` → связанный `IblockElement`
    - `F` → `File`
    - `L` → `Enumerate`
    - `N` → `int` / `float`
    - `S`, `G` → как есть

Встроенные USER\_TYPE:

USER\_TYPEРезультат`Date``DateTimeImmutable` (дата, 00:00:00)`DateTime``DateTimeImmutable`---

withResolvers — кастомные USER\_TYPE
------------------------------------

[](#withresolvers--кастомные-user_type)

Для свойств с нестандартным `b_iblock_property.USER_TYPE` (например `DependentSelect`, `booleanValue`) передайте объекты, реализующие интерфейс `UserTypePropertyResolver`.

```
use IblockElementReader\Resolve\Contract\UserTypePropertyResolver;
use IblockElementReader\Resolve\PropertyResolveContext;

final class BooleanValuePropertyResolver implements UserTypePropertyResolver
{
    public function userType(): string
    {
        return 'booleanValue';
    }

    public function resolve(PropertyResolveContext $context): array
    {
        return [
            '0'    => false,
            '1'    => true,
            ''     => false,
            'null' => false,
        ];
    }
}
```

Подключение:

```
$elements = (new IblockElementReader())
    ->filter(id: [...], iblock: [5])
    ->withProps(['IS_ACTIVE.VALUE', 'MAKE_ID.VALUE'])
    ->withResolvers([
        new BooleanValuePropertyResolver(),
        new DependentSelectPropertyResolver($makeRepo, $modelRepo),
    ])
    ->get();
```

### Контракт резолвера

[](#контракт-резолвера)

**`userType(): string`** — точное значение `b_iblock_property.USER_TYPE`. Регистр и строка должны совпадать с БД.

**`resolve(PropertyResolveContext $context): array`** — один batch-вызов на тип за весь `get()`.

`PropertyResolveContext`:

ПолеОписание`$definition`метаданные свойства (`code`, `type`, `userType`, `linkIblockId`, …)`$uniqueValues`уникальные сырые значения по всем элементам запроса`$uniqueDescriptions`уникальные description (для multiple)`$byElement``elementId => value` или `elementId => list`Возвращаемый map: **`rawValue => resolved`**. Ключи должны совпадать с тем, что лежит в `VALUE` свойства (строка `"123"`, не int `123`, если так хранит Bitrix).

Пустые single-свойства попадают в batch как `null` (в `$uniqueValues` и `$byElement`). В PHP ключи `null` и `''` в map — одно и то же; достаточно `'' => false`.

Резолвер срабатывает **только** если свойство запрошено **с `.VALUE`**.

Кастомный резолвер **переопределяет** встроенный для того же USER\_TYPE (например свой `Date` вместо стандартного парсера).

### DependentSelect и несколько свойств одного USER\_TYPE

[](#dependentselect-и-несколько-свойств-одного-user_type)

Один USER\_TYPE = один объект-резолвер. Если разные коды свойств (`MAKE_ID`, `MODEL_ID`) используют один `DependentSelect`, внутри `resolve()` разветвляйтесь по `$context->definition->code`:

```
public function resolve(PropertyResolveContext $context): array
{
    return match ($context->definition->code) {
        'MAKE_ID'  => $this->resolveMakes($context->uniqueValues),
        'MODEL_ID' => $this->resolveModels($context->uniqueValues),
        default    => [],
    };
}
```

---

Вложенные свойства (E → другой инфоблок)
----------------------------------------

[](#вложенные-свойства-e--другой-инфоблок)

Путь через точку после `.VALUE` родительского свойства типа `E`:

```
->withProps([
    'STORE.VALUE.ADDRESS.VALUE',
    'STORE.VALUE.PHONES.VALUE',
    'STORE.VALUE.NAME',           // поле связанного элемента без резолва
])
```

- `STORE.VALUE` — связанный элемент как `IblockElement` (поля из `withFields` родительского запроса)
- `STORE.VALUE.ADDRESS.VALUE` — свойство `ADDRESS` внутри связанного инфоблока, с резолвом
- Связанный элемент (E) наследует **`withFields`** верхнего запроса (`['*']`, `['NAME']`, …); nested props добавляются отдельно (`LABEL.VALUE.ICON`)
- Максимальная глубина пути — 8 сегментов

Повторные ссылки на один и тот же элемент кешируются (`LinkedElementCache`).

---

Результат
---------

[](#результат)

`get()` возвращает **`IblockElementCollection`** — типизированную domain-модель. Pipeline резолвит значения и сразу собирает `IblockElement`.

Scope проверяется в **`IblockElementReader`**: пустой filter — exception; несовпадение `iblock` у загруженных элементов — exception после fetch.

### IblockElement

[](#iblockelement)

```
$element->id;
$element->iblockId;
$element->name;
$element->code;
$element->sort;
$element->previewPicture;   // null|int|File — int без .VALUE, File с PREVIEW_PICTURE.VALUE
$element->detailPicture;    // null|int|File
$element->isActive;
$element->createdAt;       // ?DateTimeInterface
$element->properties;       // array  ключ = CODE свойства
                            // все запрошенные свойства, в т.ч. с пустым value => null
```

### IblockProperty / IblockPropertyValue

[](#iblockproperty--iblockpropertyvalue)

```
$property->definition;     // IblockPropertyDefinitionRecord
$property->value;            // IblockPropertyValue | IblockPropertyValue[] | null

$value->raw;                 // сырое значение из БД
$value->resolved;            // после .VALUE или null, если .VALUE не запрашивали
$value->description;
```

Для свойства типа E с `.VALUE` в `resolved` — вложенный `IblockElement`.

### IblockElementCollection

[](#iblockelementcollection)

```
$elements->count();
$elements->elements; // IblockElement[]
```

Используйте `first()` на reader или `$collection->elements[0]`.

---

Структура модуля
----------------

[](#структура-модуля)

**`return null` из резолвера** — нужен массив. Пустой результат: `return []`.

**Неверные ключи map** — ключ должен быть raw value из свойства. Осторожно с `array_column($rows, 'name', 'id')`: в VALUE может лежать `ID` из другой таблицы, не `id` строки.

**Забыли `.VALUE`** — резолвер не вызовется, вернётся сырое значение.

**`withProps(['*.VALUE'])` на большом инфоблоке** — загружаются и резолвятся все свойства. Для API лучше явный список кодов.

**USER\_TYPE не совпадает** — проверьте `b_iblock_property.USER_TYPE` в БД (`DependentSelect`, не `dependentSelect`).

**Multiple-свойство** — `$byElement[$id]` может быть массивом значений; map всё равно строится по `uniqueValues`.

---

Полный пример
-------------

[](#полный-пример)

```
use IblockElementReader\IblockElementReader;

final class BikeCatalogReader
{
    public function __construct(
        private int $bikeIblockId,
        private DependentSelectPropertyResolver $dependentSelect,
        private BooleanValuePropertyResolver $booleanValue,
    ) {
    }

    public function loadPromoted(array $ids): array
    {
        $elements = (new IblockElementReader())
            ->filter(id: $ids, iblock: $this->bikeIblockId)
            ->withFields(['ID', 'NAME', 'CODE', 'PREVIEW_PICTURE.VALUE'])
            ->withProps([
                'MAKE_ID.VALUE',
                'MODEL_ID.VALUE',
                'PRICE.VALUE',
                'IS_NEW.VALUE',
                'STORE.VALUE.ADDRESS.VALUE',
            ])
            ->withResolvers([
                $this->dependentSelect,
                $this->booleanValue,
            ])
            ->get();

        return $elements->elements;
    }
}
```

---

Структура пакета
----------------

[](#структура-пакета)

```
src/
├── IblockElementReader.php      fluent API (filter, withFields, withProps), scope validation
├── IblockElementFetcher.php     оркестрация load → resolve
├── Model/                       domain-модель
│   ├── IblockElement.php
│   ├── IblockElementCollection.php
│   ├── IblockProperty.php
│   ├── IblockPropertyValue.php
│   ├── Enumerate.php            resolved L (список)
│   ├── File.php                 resolved F (файл)
│   └── Record/                  сырые данные из БД (load → resolve)
│       ├── IblockElementRecord.php
│       ├── IblockElementRecordCollection.php
│       ├── IblockPropertyDefinitionRecord.php
│       ├── IblockPropertyValueRecord.php
│       └── IblockPropertyValueRecordCollection.php
├── Load/                        orchestration (specs → records)
│   └── ElementLoader.php
├── Repository/                  обращения к БД
│   ├── IblockElementRepository.php
│   ├── IblockPropertyDefinitionRepository.php
│   ├── IblockPropertyValueRepository.php
│   ├── FileRepository.php
│   └── EnumerateRepository.php
└── Resolve/
    ├── Contract/
    │   └── UserTypePropertyResolver.php
    ├── LinkedElementCache.php   flyweight linked E на один get()
    ├── PropertyResolverRegistry.php
    ├── ResolvePipeline.php      resolve + сборка IblockElement
    └── BuiltIn/                 Date, DateTime, File, Enum, Number, …

tests/                           unit-тесты (без Bitrix runtime)

```

---

Install
-------

[](#install)

composer require jiscariot/iblock-element-reader

###  Health Score

21

—

LowBetter than 17% of packages

Maintenance60

Regular maintenance activity

Popularity7

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity11

Early-stage or recently created project

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

### Community

Maintainers

![](https://www.gravatar.com/avatar/999f322438de77281b2cad5dfeca659e02dc188c72e4e92df9fe11baafee1d26?d=identicon)[JIscariot](/maintainers/JIscariot)

---

Top Contributors

[![JIscariot](https://avatars.githubusercontent.com/u/15903816?v=4)](https://github.com/JIscariot "JIscariot (1 commits)")

### Embed Badge

![Health badge](/badges/jiscariot-iblock-element-reader/health.svg)

```
[![Health](https://phpackages.com/badges/jiscariot-iblock-element-reader/health.svg)](https://phpackages.com/packages/jiscariot-iblock-element-reader)
```

###  Alternatives

[jdorn/sql-formatter

a PHP SQL highlighting library

3.8k117.8M121](/packages/jdorn-sql-formatter)[backup-manager/backup-manager

A framework agnostic database backup manager with user-definable procedures and support for S3, Dropbox, FTP, SFTP, and more with drivers for popular frameworks.

1.7k1.6M11](/packages/backup-manager-backup-manager)[propel/propel1

Propel is an open-source Object-Relational Mapping (ORM) for PHP5.

8351.6M88](/packages/propel-propel1)[insolita/yii2-migration-generator

Set of gii tools for generating files for migration by schema of table , phpdoc or table data

108508.0k5](/packages/insolita-yii2-migration-generator)[ichikaway/cakephp-mongodb

MongoDB Datasource for CakePHP

3388.2k](/packages/ichikaway-cakephp-mongodb)[xpdo/xpdo

A PDO-based Object/Relational Bridge Library

7088.4k4](/packages/xpdo-xpdo)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
