Полный цикл в digital

Построитель запросов ORM

Реализация ORM в ядре D7 — очередная интересная, перспективная, но как обычно плохо документированная разработка от 1с-Битрикс. В первую очередь призвана абстрагировать разработчика от механики работы с таблицами на уровне запросов к БД, введя понятие:

  • Сущности
  • Поля сущности

Интересовало меня как общее устройство системы и принцип постороения кода, так и частные случаи запросов.

Для начала изучаем понятия сущности, поля сущности и датаменеджера в документации.

  • Cущность в терминах битрикса, это таблица
  • Поля сущности столбцы или ссылки на другие сущности
  • Датаменеджер система управления данными

Для каждой сущности нужно создать описание, например:

/bitrix/modules/iblock/lib/element.php<?php
namespace Bitrix\Iblock;
use Bitrix\Main,
    Bitrix\Main\Localization\Loc;
Loc::loadMessages(__FILE__);
/**
* Class ElementTable
* 
* Fields:
* ID int mandatory
* TIMESTAMP_X datetime optional
* MODIFIED_BY int optional
* DATE_CREATE datetime optional
* CREATED_BY int optional
* IBLOCK_ID int mandatory
* IBLOCK_SECTION_ID int optional
* ACTIVE bool optional default 'Y'
* ACTIVE_FROM datetime optional
* ACTIVE_TO datetime optional
* SORT int optional default 500
* NAME string(255) mandatory
* PREVIEW_PICTURE int optional
* PREVIEW_TEXT string optional
* PREVIEW_TEXT_TYPE enum ('text', 'html') optional default 'text'
* DETAIL_PICTURE int optional
* DETAIL_TEXT string optional
* DETAIL_TEXT_TYPE enum ('text', 'html') optional default 'text'
* SEARCHABLE_CONTENT string optional
* WF_STATUS_ID int optional default 1
* WF_PARENT_ELEMENT_ID int optional
* WF_NEW string(1) optional
* WF_LOCKED_BY int optional
* WF_DATE_LOCK datetime optional
* WF_COMMENTS string optional
* IN_SECTIONS bool optional default 'N'
* XML_ID string(255) optional
* CODE string(255) optional
* TAGS string(255) optional
* TMP_ID string(40) optional
* WF_LAST_HISTORY_ID int optional
* SHOW_COUNTER int optional
* SHOW_COUNTER_START datetime optional
* PREVIEW_PICTURE reference to {@link \Bitrix\File\FileTable}
* DETAIL_PICTURE reference to {@link \Bitrix\File\FileTable}
* IBLOCK reference to {@link \Bitrix\Iblock\IblockTable}
* WF_PARENT_ELEMENT reference to {@link \Bitrix\Iblock\IblockElementTable}
* IBLOCK_SECTION reference to {@link \Bitrix\Iblock\IblockSectionTable}
* MODIFIED_BY reference to {@link \Bitrix\User\UserTable}
* CREATED_BY reference to {@link \Bitrix\User\UserTable}
* WF_LOCKED_BY reference to {@link \Bitrix\User\UserTable}
*
* @package Bitrix\Iblock
**/
class ElementTable extends Main\Entity\DataManager
{
    /**
    * Returns DB table name for entity.
    *
    * @return string
    */
    public static function getTableName()
    {
        return 'b_iblock_element';
    }
    /**
    * Returns entity map definition.
    *
    * @return array
    */
    public static function getMap()
    {
        return array(
            'ID' => array( // Идентификатор
                'data_type' => 'integer',
                'primary' => true,
                'autocomplete' => true,
                'title' => Loc::getMessage('ELEMENT_ENTITY_ID_FIELD'),
            ),
            'TIMESTAMP_X' => array( // Дата изменения
                'data_type' => 'datetime',
                'title' => Loc::getMessage('ELEMENT_ENTITY_TIMESTAMP_X_FIELD'),
            ),
            'MODIFIED_BY' => array( // Кто изменил
                'data_type' => 'integer',
                'title' => Loc::getMessage('ELEMENT_ENTITY_MODIFIED_BY_FIELD'),
            ),
            'DATE_CREATE' => array( // Дата создания
                'data_type' => 'datetime',
                'title' => Loc::getMessage('ELEMENT_ENTITY_DATE_CREATE_FIELD'),
            ),
            'CREATED_BY' => array( // Кто создал
                'data_type' => 'integer',
                'title' => Loc::getMessage('ELEMENT_ENTITY_CREATED_BY_FIELD'),
            ),
            'IBLOCK_ID' => array( // Идентификатор инфоблока
                'data_type' => 'integer',
                'required' => true,
                'title' => Loc::getMessage('ELEMENT_ENTITY_IBLOCK_ID_FIELD'),
            ),
            'IBLOCK_SECTION_ID' => array( // Основной раздел
                'data_type' => 'integer',
                'title' => Loc::getMessage('ELEMENT_ENTITY_IBLOCK_SECTION_ID_FIELD'),
            ),
            'ACTIVE' => array( // Активность
                'data_type' => 'boolean',
                'values' => array('N', 'Y'),
                'title' => Loc::getMessage('ELEMENT_ENTITY_ACTIVE_FIELD'),
            ),
            'ACTIVE_FROM' => array( // Дата начала активности
                'data_type' => 'datetime',
                'title' => Loc::getMessage('ELEMENT_ENTITY_ACTIVE_FROM_FIELD'),
            ),
            'ACTIVE_TO' => array( // Дата окончания активности
                'data_type' => 'datetime',
                'title' => Loc::getMessage('ELEMENT_ENTITY_ACTIVE_TO_FIELD'),
            ),
            'SORT' => array( // Индекс сортировки
                'data_type' => 'integer',
                'title' => Loc::getMessage('ELEMENT_ENTITY_SORT_FIELD'),
            ),
            'NAME' => array( // Наименование
                'data_type' => 'string',
                'required' => true,
                'validation' => array(__CLASS__, 'validateName'),
                'title' => Loc::getMessage('ELEMENT_ENTITY_NAME_FIELD'),
            ),
            'PREVIEW_PICTURE' => array( // Картинка анонса
                'data_type' => 'integer',
                'title' => Loc::getMessage('ELEMENT_ENTITY_PREVIEW_PICTURE_FIELD'),
            ),
            'PREVIEW_TEXT' => array( // Описание для анонса
                'data_type' => 'text',
                'title' => Loc::getMessage('ELEMENT_ENTITY_PREVIEW_TEXT_FIELD'),
            ),
            'PREVIEW_TEXT_TYPE' => array( // Тип описания для анонса
                'data_type' => 'enum',
                'values' => array('text', 'html'),
                'title' => Loc::getMessage('ELEMENT_ENTITY_PREVIEW_TEXT_TYPE_FIELD'),
            ),
            'DETAIL_PICTURE' => array( // Детальная картинка
                'data_type' => 'integer',
                'title' => Loc::getMessage('ELEMENT_ENTITY_DETAIL_PICTURE_FIELD'),
            ),
            'DETAIL_TEXT' => array( // Детальное описание
                'data_type' => 'text',
                'title' => Loc::getMessage('ELEMENT_ENTITY_DETAIL_TEXT_FIELD'),
            ),
            'DETAIL_TEXT_TYPE' => array( // Тип детального описания
                'data_type' => 'enum',
                'values' => array('text', 'html'),
                'title' => Loc::getMessage('ELEMENT_ENTITY_DETAIL_TEXT_TYPE_FIELD'),
            ),
            'SEARCHABLE_CONTENT' => array( // Поисковый индекс
                'data_type' => 'text',
                'title' => Loc::getMessage('ELEMENT_ENTITY_SEARCHABLE_CONTENT_FIELD'),
            ),
            'WF_STATUS_ID' => array( // Статус в документообороте
                'data_type' => 'integer',
                'title' => Loc::getMessage('ELEMENT_ENTITY_WF_STATUS_ID_FIELD'),
            ),
            'WF_PARENT_ELEMENT_ID' => array( // Элемент-родитель
                'data_type' => 'integer',
                'title' => Loc::getMessage('ELEMENT_ENTITY_WF_PARENT_ELEMENT_ID_FIELD'),
            ),
            'WF_NEW' => array( // Флаг публикации черновика
                'data_type' => 'string',
                'validation' => array(__CLASS__, 'validateWfNew'),
                'title' => Loc::getMessage('ELEMENT_ENTITY_WF_NEW_FIELD'),
            ),
            'WF_LOCKED_BY' => array( // Кто заблокировал
                'data_type' => 'integer',
                'title' => Loc::getMessage('ELEMENT_ENTITY_WF_LOCKED_BY_FIELD'),
            ),
            'WF_DATE_LOCK' => array( // Дата блокировки
                'data_type' => 'datetime',
                'title' => Loc::getMessage('ELEMENT_ENTITY_WF_DATE_LOCK_FIELD'),
            ),
            'WF_COMMENTS' => array( // Комментарий документооборота
                'data_type' => 'text',
                'title' => Loc::getMessage('ELEMENT_ENTITY_WF_COMMENTS_FIELD'),
            ),
            'IN_SECTIONS' => array( // Входит в разделы инфоблока
                'data_type' => 'boolean',
                'values' => array('N', 'Y'),
                'title' => Loc::getMessage('ELEMENT_ENTITY_IN_SECTIONS_FIELD'),
            ),
            'XML_ID' => array( // Внешний код
                'data_type' => 'string',
                'validation' => array(__CLASS__, 'validateXmlId'),
                'title' => Loc::getMessage('ELEMENT_ENTITY_XML_ID_FIELD'),
            ),
            'CODE' => array( // Символьный код
                'data_type' => 'string',
                'validation' => array(__CLASS__, 'validateCode'),
                'title' => Loc::getMessage('ELEMENT_ENTITY_CODE_FIELD'),
            ),
            'TAGS' => array( // Теги
                'data_type' => 'string',
                'validation' => array(__CLASS__, 'validateTags'),
                'title' => Loc::getMessage('ELEMENT_ENTITY_TAGS_FIELD'),
            ),
            'TMP_ID' => array( // Временный код
                'data_type' => 'string',
                'validation' => array(__CLASS__, 'validateTmpId'),
                'title' => Loc::getMessage('ELEMENT_ENTITY_TMP_ID_FIELD'),
            ),
            'WF_LAST_HISTORY_ID' => array(
                'data_type' => 'integer',
                'title' => Loc::getMessage('ELEMENT_ENTITY_WF_LAST_HISTORY_ID_FIELD'),
            ),
            'SHOW_COUNTER' => array( // Количество показов
                'data_type' => 'integer',
                'title' => Loc::getMessage('ELEMENT_ENTITY_SHOW_COUNTER_FIELD'),
            ),
            'SHOW_COUNTER_START' => array( // Дата первого показа
                'data_type' => 'datetime',
                'title' => Loc::getMessage('ELEMENT_ENTITY_SHOW_COUNTER_START_FIELD'),
            ),
            'PREVIEW_PICTURE' => array(
                'data_type' => 'Bitrix\File\File',
                'reference' => array('=this.PREVIEW_PICTURE' => 'ref.ID'),
            ),
            'DETAIL_PICTURE' => array(
                'data_type' => 'Bitrix\File\File',
                'reference' => array('=this.DETAIL_PICTURE' => 'ref.ID'),
            ),
            'IBLOCK' => array(
                'data_type' => 'Bitrix\Iblock\Iblock',
                'reference' => array('=this.IBLOCK_ID' => 'ref.ID'),
            ),
            'WF_PARENT_ELEMENT' => array(
                'data_type' => 'Bitrix\Iblock\IblockElement',
                'reference' => array('=this.WF_PARENT_ELEMENT_ID' => 'ref.ID'),
            ),
            'IBLOCK_SECTION' => array(
                'data_type' => 'Bitrix\Iblock\IblockSection',
                'reference' => array('=this.IBLOCK_SECTION_ID' => 'ref.ID'),
            ),
            'MODIFIED_BY' => array(
                'data_type' => 'Bitrix\User\User',
                'reference' => array('=this.MODIFIED_BY' => 'ref.ID'),
            ),
            'CREATED_BY' => array(
                'data_type' => 'Bitrix\User\User',
                'reference' => array('=this.CREATED_BY' => 'ref.ID'),
            ),
            'WF_LOCKED_BY' => array(
                'data_type' => 'Bitrix\User\User',
                'reference' => array('=this.WF_LOCKED_BY' => 'ref.ID'),
            ),
        );
    }
    /**
    * Returns validators for NAME field.
    *
    * @return array
    */
    public static function validateName()
    {
        return array(
            new Main\Entity\Validator\Length(null, 255),
        );
    }
    /**
    * Returns validators for WF_NEW field.
    *
    * @return array
    */
    public static function validateWfNew()
    {
        return array(
            new Main\Entity\Validator\Length(null, 1),
        );
    }
    /**
    * Returns validators for XML_ID field.
    *
    * @return array
    */
    public static function validateXmlId()
    {
        return array(
            new Main\Entity\Validator\Length(null, 255),
        );
    }
    /**
    * Returns validators for CODE field.
    *
    * @return array
    */
    public static function validateCode()
    {
        return array(
            new Main\Entity\Validator\Length(null, 255),
        );
    }
    /**
    * Returns validators for TAGS field.
    *
    * @return array
    */
    public static function validateTags()
    {
        return array(
            new Main\Entity\Validator\Length(null, 255),
        );
    }
    /**
    * Returns validators for TMP_ID field.
    *
    * @return array
    */
    public static function validateTmpId()
    {
        return array(
            new Main\Entity\Validator\Length(null, 40),
        );
    }
}

Класс описывает таблицу БД b_iblock_element, которая хранит элементы инфоблоков:

--
-- Структура таблицы `b_iblock_element`
--
CREATE TABLE `b_iblock_element` (
`ID` int(11) NOT NULL,
`TIMESTAMP_X` datetime DEFAULT NULL,
`MODIFIED_BY` int(18) DEFAULT NULL,
`DATE_CREATE` datetime DEFAULT NULL,
`CREATED_BY` int(18) DEFAULT NULL,
`IBLOCK_ID` int(11) NOT NULL DEFAULT '0',
`IBLOCK_SECTION_ID` int(11) DEFAULT NULL,
`ACTIVE` char(1) COLLATE utf8_unicode_ci NOT NULL DEFAULT 'Y',
`ACTIVE_FROM` datetime DEFAULT NULL,
`ACTIVE_TO` datetime DEFAULT NULL,
`SORT` int(11) NOT NULL DEFAULT '500',
`NAME` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`PREVIEW_PICTURE` int(18) DEFAULT NULL,
`PREVIEW_TEXT` text COLLATE utf8_unicode_ci,
`PREVIEW_TEXT_TYPE` varchar(4) COLLATE utf8_unicode_ci NOT NULL DEFAULT 'text',
`DETAIL_PICTURE` int(18) DEFAULT NULL,
`DETAIL_TEXT` longtext COLLATE utf8_unicode_ci,
`DETAIL_TEXT_TYPE` varchar(4) COLLATE utf8_unicode_ci NOT NULL DEFAULT 'text',
`SEARCHABLE_CONTENT` text COLLATE utf8_unicode_ci,
`WF_STATUS_ID` int(18) DEFAULT '1',
`WF_PARENT_ELEMENT_ID` int(11) DEFAULT NULL,
`WF_NEW` char(1) COLLATE utf8_unicode_ci DEFAULT NULL,
`WF_LOCKED_BY` int(18) DEFAULT NULL,
`WF_DATE_LOCK` datetime DEFAULT NULL,
`WF_COMMENTS` text COLLATE utf8_unicode_ci,
`IN_SECTIONS` char(1) COLLATE utf8_unicode_ci NOT NULL DEFAULT 'N',
`XML_ID` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
`CODE` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
`TAGS` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
`TMP_ID` varchar(40) COLLATE utf8_unicode_ci DEFAULT NULL,
`WF_LAST_HISTORY_ID` int(11) DEFAULT NULL,
`SHOW_COUNTER` int(18) DEFAULT NULL,
`SHOW_COUNTER_START` datetime DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
--
-- Индексы таблицы `b_iblock_element`
--
ALTER TABLE `b_iblock_element`
ADD PRIMARY KEY (`ID`),
ADD KEY `ix_iblock_element_1` (`IBLOCK_ID`,`IBLOCK_SECTION_ID`),
ADD KEY `ix_iblock_element_4` (`IBLOCK_ID`,`XML_ID`,`WF_PARENT_ELEMENT_ID`),
ADD KEY `ix_iblock_element_3` (`WF_PARENT_ELEMENT_ID`),
ADD KEY `ix_iblock_element_code` (`IBLOCK_ID`,`CODE`);
--
-- AUTO_INCREMENT для таблицы `b_iblock_element`
--
ALTER TABLE `b_iblock_element`
MODIFY `ID` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=357;
COMMIT;

В getMap перечислены все поля таблицы, включая описание связей с другими сущностями. В примере таким образом указано отношение столбца IBLOCK_ID текущей таблицы и столбца ID сущности Iblock. В дальнейшем по reference полям возможно выбирать поля связанных сущностей и использовать их в фильтрах.

'IBLOCK' => array(
    'data_type' => 'Bitrix\Iblock\Iblock',
    'reference' => array('=this.IBLOCK_ID' => 'ref.ID'),
  )

С какими сущностями работает построитель запросов

Всё что написано ниже, применимо и к другим модулям, только названия таблиц будут другие. Многое можно узнать из файла /bitrix/modules/main/lib/orm/data/datamanager.php

Подключаем модуль:

\Bitrix\Main\Loader::includeModule('iblock');

После подключения модуля, нам становится доступен целый набор различных классов и методов для работы с данными инфоблоков. Существуют и другие методы для работы с инфоблоками, и это лишь часть из них. Наиболее часто используемые методы, это получение списков записей:

  1. \Bitrix\Iblock\TypeTable::getEntity(); типы инфоблоков
  2. \Bitrix\Iblock\IblockTable::getEntity(); инфоблоки
  3. \Bitrix\Iblock\PropertyTable::getEntity(); свойства инфоблоков
  4. \Bitrix\Iblock\PropertyEnumerationTable::getEntity(); значения свойств, например списков
  5. \Bitrix\Iblock\SectionTable::getEntity(); разделы инфоблоков
  6. \Bitrix\Iblock\ElementTable::getEntity(); элементы инфоблоков
  7. \Bitrix\Iblock\InheritedPropertyTable::getEntity(); наследуемые свойства, seo шаблоны

Автоматическая генерация класса

Для использования генератора ORM классов перейдите на страницу Настройки -> Настройки продукта -> Настройки модулей -> Монитор производительности модуль Монитор производительности должен быть установлен. На вкладке Генератор таблетов отметьте поле Разрешить генерацию таблетов для ORM.

Автоматически сгенерировать класс с описанием любой таблицы можно на странице Настройки -> Производительность -> Таблицы, в меню действий доступен пункт ORM

Доступные методы

Список методов Bitrix\Main\Entity\Query:

Получение данных

  • setSelect(), setGroup() устанавливает массив с именами полей
  • addSelect(), addGroup() добавляет имя поля
  • getSelect(), getGroup() возвращает массив с именами полей

Фильтрация данных

  • setFilter() устанавливает одно или многомерный массив с описанием фильтра
  • addFilter() добавляет один параметр фильтра со значением
  • getFilter() возвращает текущее описание фильтра

Сортировка данных

  • setOrder() устанавливает массив с именами полей и порядком сортировки
  • addOrder() добавляет одно поле с порядком сортировки
  • getOrder() возвращает текущее описание сортировки

Ограничение количества данных

  • setLimit(), setOffset() устанавливает значение
  • getLimit(), getOffset() возвращает текущее значение

Временное поле

  • registerRuntimeField() регистрирует новое временное поле для исходной сущности

Выполнение запросов

Выборка

Выбираем элементы инфоблока с идентификатором 5:

Bitrix\Main\Loader::includeModule('iblock');
// создаем объект Query, в качестве параметра передаем объект сущности (элемент инфоблока)
$query = new Bitrix\Main\Entity\Query(
    Bitrix\Iblock\ElementTable::getEntity()
);
// выбираем идентификатор элемента, символьный код и наименование
$query->setSelect(array('ID', 'CODE', 'NAME'))
    // идентификатор инфоблока равен 5
    ->setFilter(array('IBLOCK_ID' => 5))
    // сортируем элементы по идентификатору, по возрастанию
    ->setOrder(array('ID' => 'ASC'))
    // выбираем только три элемента
    ->setLimit(3);
// посмотрим, какой запрос был сформирован
echo '<pre>' . $query->getQuery() . '</pre>';
// выполняем запрос
$result = $query->exec();
// выводим результат
while ($row = $result->fetch()) {
    pp($row);
}

Запрос:

SELECT
`iblock_element`.`ID` AS `ID`,
`iblock_element`.`CODE` AS `CODE`,
`iblock_element`.`NAME` AS `NAME`
FROM
`b_iblock_element` `iblock_element`
WHERE
`iblock_element`.`IBLOCK_ID` = 5
ORDER BY
`ID` ASC
LIMIT
0, 3

Ответ:

Array
(
    [ID] => 347
    [CODE] => angliyskiy-buldog
    [NAME] => Английский бульдог
)
Array
(
    [ID] => 348
    [CODE] => dalmatin
    [NAME] => Далматин
)
Array
(
    [ID] => 349
    [CODE] => afganskaya-borzaya
    [NAME] => Афганская борзая
)

Соединение

Например через сущность ElementTable, можно выбирать или ставить условия на поля связанной сущности, в данном примере это IBLOCK. Связанная таблица по умолчанию присоединяется с помощью LEFT JOIN. Вспомним reference поле IBLOCK в описании ElementTable:

'IBLOCK' => array(
'data_type' => 'Bitrix\Iblock\Iblock',
'reference' => array('=this.IBLOCK_ID' => 'ref.ID'),
)

Выберем данные самого инфоблока вместе с элементами инфоблока:

Bitrix\Main\Loader::includeModule('iblock');
// создаем объект Query, в качестве параметра передаем объект сущности (элемент инфоблока)
$query = new Bitrix\Main\Entity\Query(
    Bitrix\Iblock\ElementTable::getEntity()
);
// выборка
$query->setSelect(array('ID', 'CODE', 'NAME', 'IBLOCK.ID', 'IBLOCK.CODE', 'IBLOCK.NAME'))
    // за место этого
    // ->setFilter(array('IBLOCK_ID' => 5))
    // можно так
    ->setFilter(array('IBLOCK.ID' => 5))
    // сортировка
    ->setOrder(array('ID' => 'ASC'))
    // лимит
    ->setLimit(3);
// посмотрим, какой запрос был сформирован
echo '<pre>' . $query->getQuery() . '</pre>';
// выполняем запрос
$result = $query->exec();
// выводим результат
while ($row = $result->fetch()) {
    pp($row);
}

Запрос:

SELECT
`iblock_element`.`ID` AS `ID`,
`iblock_element`.`CODE` AS `CODE`,
`iblock_element`.`NAME` AS `NAME`,
`iblock_element_iblock`.`ID` AS `IBLOCK_ELEMENT_IBLOCK_ID`,
`iblock_element_iblock`.`CODE` AS `IBLOCK_ELEMENT_IBLOCK_CODE`,
`iblock_element_iblock`.`NAME` AS `IBLOCK_ELEMENT_IBLOCK_NAME`
FROM
`b_iblock_element` `iblock_element` LEFT JOIN `b_iblock` `iblock_element_iblock`
ON `iblock_element`.`IBLOCK_ID` = `iblock_element_iblock`.`ID`
WHERE
`iblock_element_iblock`.`ID` = 5
ORDER BY
`ID` ASC
LIMIT
0, 3

Ответ:

Array
(
    [ID] => 347
    [CODE] => angliyskiy-buldog
    [NAME] => Английский бульдог
    [IBLOCK_ELEMENT_IBLOCK_ID] => 5
    [IBLOCK_ELEMENT_IBLOCK_CODE] => articles
    [IBLOCK_ELEMENT_IBLOCK_NAME] => Статьи о домашних животных
)
Array
(
    [ID] => 348
    [CODE] => dalmatin
    [NAME] => Далматин
    [IBLOCK_ELEMENT_IBLOCK_ID] => 5
    [IBLOCK_ELEMENT_IBLOCK_CODE] => articles
    [IBLOCK_ELEMENT_IBLOCK_NAME] => Статьи о домашних животных
)
Array
(
    [ID] => 349
    [CODE] => afganskaya-borzaya
    [NAME] => Афганская борзая
    [IBLOCK_ELEMENT_IBLOCK_ID] => 5
    [IBLOCK_ELEMENT_IBLOCK_CODE] => articles
    [IBLOCK_ELEMENT_IBLOCK_NAME] => Статьи о домашних животных
)

Соединение со сложной логикой

В определении runtime-reference поля можно указывать тип соединения LEFT, RIGHT, INNER, в фильтре использовать сложную логику, как в CIblockElement::GetList():

Bitrix\Main\Loader::includeModule('iblock');
// создаем объект Query, в качестве параметра передаем объект сущности (инфоблок)
$query = new Bitrix\Main\Entity\Query(
    Bitrix\Iblock\IblockTable::getEntity()
);
// регистрируем новое временное поле для исходной сущности
$query->registerRuntimeField(
    // поле element как ссылка на таблицу b_iblock_element
    'element',
    array(
        // тип — сущность ElementTable
        'data_type' => 'Bitrix\Iblock\ElementTable',
        // this.ID относится к таблице, относительно которой строится запрос, т.е. b_iblock.ID = b_iblock_element.IBLOCK_ID
        'reference' => array('=this.ID' => 'ref.IBLOCK_ID'),
        // тип соединения INNER JOIN
        'join_type' => 'INNER'
    )
);
// регистрируем новое временное поле для исходной сущности
$query->registerRuntimeField(
    // поле type как ссылка на таблицу b_iblock_type
    'type',
    array(
        'data_type' => 'Bitrix\Iblock\TypeTable',
        'reference' => array('=this.IBLOCK_TYPE_ID' => 'ref.ID'),
        'join_type' => 'INNER'
    )
);
// выбираем название инфоблока, символьный код инфоблока, название элемента, символьный код элемента и идентификатор типа инфоблока
$query->setSelect(array('NAME', 'CODE', 'element.NAME', 'element.CODE', 'type.ID'));
// выбираем элементы с идентификаторами 348 или 349
$query->setFilter(
    array(
        'LOGIC' => 'OR',
        array('element.ID' => 348),
        array('element.ID' => 349),
    )
);
// посмотрим, какой запрос был сформирован
echo '<pre>' . $query->getQuery() . '</pre>';
// выполняем запрос
$result = $query->exec();
// выводим результат
while ($row = $result->fetch()) {
    pp($row);
}

Запрос:

SELECT
`iblock_iblock`.`NAME` AS `NAME`,
`iblock_iblock`.`CODE` AS `CODE`,
`iblock_iblock_element`.`NAME` AS `IBLOCK_IBLOCK_element_NAME`,
`iblock_iblock_element`.`CODE` AS `IBLOCK_IBLOCK_element_CODE`,
`iblock_iblock_type`.`ID` AS `IBLOCK_IBLOCK_type_ID`
FROM
`b_iblock` `iblock_iblock`
INNER JOIN `b_iblock_element` `iblock_iblock_element`
ON `iblock_iblock`.`ID` = `iblock_iblock_element`.`IBLOCK_ID`
INNER JOIN `b_iblock_type` `iblock_iblock_type`
ON `iblock_iblock`.`IBLOCK_TYPE_ID` = `iblock_iblock_type`.`ID`
WHERE
(`iblock_iblock_element`.`ID` = 348) OR (`iblock_iblock_element`.`ID` = 349)

Ответ:

Array
(
    [NAME] => Статьи о домашних животных
    [CODE] => articles
    [IBLOCK_IBLOCK_element_NAME] => Далматин
    [IBLOCK_IBLOCK_element_CODE] => dalmatin
    [IBLOCK_IBLOCK_type_ID] => content
)
Array
(
    [NAME] => Статьи о домашних животных
    [CODE] => articles
    [IBLOCK_IBLOCK_element_NAME] => Афганская борзая
    [IBLOCK_IBLOCK_element_CODE] => afganskaya-borzaya
    [IBLOCK_IBLOCK_type_ID] => content
)

Агрегатные функции

В запросах можно использовать агрегатные функции MySQL. Для это служит метод registerRuntimeField(), регистрирующий новое поле на время выполнения запроса. Посмотрим, сколько активных элементов в инфоблоке:

Bitrix\Main\Loader::includeModule('iblock');
// создаем объект Query, в качестве параметра передаем объект сущности (элемент инфоблока)
$query = new Bitrix\Main\Entity\Query(
    Bitrix\Iblock\ElementTable::getEntity()
);
// регистрируем новое временное поле для исходной сущности
$query->registerRuntimeField(
    'ACTIVE_ELEMENTS',
    array(
        // тип вычисляемого поля
        'data_type' => 'string',
        // агрегатная функция (COUNT, MAX, MIN, SUM, AVG) и поле для подстановки
        'expression' => array('GROUP_CONCAT(%s)', 'NAME')
    )
);
// выборка
$query->setSelect(array('IBLOCK.NAME', 'ACTIVE_ELEMENTS'));
// фильтр
$query->setFilter(array('IBLOCK.ID' => 5, '=ACTIVE' => 'Y'));
// посмотрим, какой запрос был сформирован
echo '<pre>' . $query->getQuery() . '</pre>';
// выполняем запрос
$result = $query->exec();
// выводим результат
while ($row = $result->fetch()) {
    pp($row);
}

Запрос:

SELECT
`iblock_element_iblock`.`NAME` AS `IBLOCK_ELEMENT_IBLOCK_NAME`,
GROUP_CONCAT(`iblock_element`.`NAME`) AS `ACTIVE_ELEMENTS`
FROM
`b_iblock_element` `iblock_element` LEFT JOIN `b_iblock` `iblock_element_iblock`
ON `iblock_element`.`IBLOCK_ID` = `iblock_element_iblock`.`ID`
WHERE
`iblock_element_iblock`.`ID` = 5 AND `iblock_element`.`ACTIVE` = 'Y'
GROUP BY
`iblock_element_iblock`.`NAME`

Ответ:

Array
(
    [IBLOCK_ELEMENT_IBLOCK_NAME] => Статьи о домашних животных
    [ACTIVE_ELEMENTS] => Английский бульдог,Далматин,Афганская борзая,Абиссинская кошка,Сиамская кошка,Американский бобтейл,Британская короткошерстная,Лабрадор,Лайка
)

Подсчет

Выбираем разделы инфоблока с идентифкатором 5 и подсчитываем количество элементов в каждом, учитываем только активные разделы и элементы:

Bitrix\Main\Loader::includeModule('iblock');
// создаем объект Query, в качестве параметра передаем объект сущности (элемент инфоблока)
$query = new Bitrix\Main\Entity\Query(
    Bitrix\Iblock\ElementTable::getEntity()
);
// регистрируем новое временное поле для исходной сущности
$query->registerRuntimeField(
    'ELEMENT_COUNT',
    array(
        // тип вычисляемого поля
        'data_type' => 'integer',
        // агрегатная функция (COUNT, MAX, MIN, SUM, AVG) и поле для подстановки
        'expression' => array('COUNT(%s)', 'NAME')
    )
);
// регистрируем новое временное поле для исходной сущности
$query->registerRuntimeField(
    'ELEMENT_LIST',
    array(
        // тип вычисляемого поля
        'data_type' => 'string',
        // агрегатная функция (COUNT, MAX, MIN, SUM, AVG) и поле для подстановки
        'expression' => array('GROUP_CONCAT(%s)', 'NAME')
    )
);
// выборка
$query->setSelect(array('IBLOCK_SECTION.NAME', 'ELEMENT_COUNT', 'ELEMENT_LIST'));
// учитываем только активные разделы и активные элементы
$query->setFilter(array('=ACTIVE' => 'Y', '=IBLOCK_SECTION.ACTIVE' => 'Y'));
// выбираем только разделы инфоблока с идентификатором 5
$query->addFilter('IBLOCK.ID', 5);
// посмотрим, какой запрос был сформирован
echo '<pre>' . $query->getQuery() . '</pre>';
// выполняем запрос
$result = $query->exec();
// выводим результат
while ($row = $result->fetch()) {
    pp($row);
}

Запрос:

SELECT
`iblock_element_iblock_section`.`NAME` AS `IBLOCK_ELEMENT_IBLOCK_SECTION_NAME`,
COUNT(`iblock_element`.`NAME`) AS `ELEMENT_COUNT`,
GROUP_CONCAT(`iblock_element`.`NAME`) AS `ELEMENT_LIST`
FROM
`b_iblock_element` `iblock_element`
LEFT JOIN `b_iblock_section` `iblock_element_iblock_section`
ON `iblock_element`.`IBLOCK_SECTION_ID` = `iblock_element_iblock_section`.`ID`
LEFT JOIN `b_iblock` `iblock_element_iblock`
ON `iblock_element`.`IBLOCK_ID` = `iblock_element_iblock`.`ID`
WHERE
`iblock_element`.`ACTIVE` = 'Y' AND
`iblock_element_iblock_section`.`ACTIVE` = 'Y' AND
`iblock_element_iblock`.`ID` = 5
GROUP BY
`iblock_element_iblock_section`.`NAME`

Ответ:

Array
(
    [IBLOCK_ELEMENT_IBLOCK_SECTION_NAME] => Породы кошек
    [ELEMENT_COUNT] => 4
    [ELEMENT_LIST] => Абиссинская кошка,Сиамская кошка,Американский бобтейл,Британская короткошерстная
)
Array
(
    [IBLOCK_ELEMENT_IBLOCK_SECTION_NAME] => Породы собак
    [ELEMENT_COUNT] => 3
    [ELEMENT_LIST] => Английский бульдог,Далматин,Афганская борзая
)
Array
(
    [IBLOCK_ELEMENT_IBLOCK_SECTION_NAME] => Служебные породы
    [ELEMENT_COUNT] => 2
    [ELEMENT_LIST] => Лабрадор,Лайка
)

Добавим еще одно условие, чтобы выбирать только разделы, содержащие более трех элементов:

$query->addFilter('>ELEMENT_COUNT', 3);

Запрос:

SELECT
`iblock_element_iblock_section`.`NAME` AS `IBLOCK_ELEMENT_IBLOCK_SECTION_NAME`,
COUNT(`iblock_element`.`NAME`) AS `ELEMENT_COUNT`,
GROUP_CONCAT(`iblock_element`.`NAME`) AS `ELEMENT_LIST`
FROM
`b_iblock_element` `iblock_element`
LEFT JOIN `b_iblock_section` `iblock_element_iblock_section`
ON `iblock_element`.`IBLOCK_SECTION_ID` = `iblock_element_iblock_section`.`ID`
LEFT JOIN `b_iblock` `iblock_element_iblock`
ON `iblock_element`.`IBLOCK_ID` = `iblock_element_iblock`.`ID`
WHERE
`iblock_element`.`ACTIVE` = 'Y' AND
`iblock_element_iblock_section`.`ACTIVE` = 'Y' AND
`iblock_element_iblock`.`ID` = 5
GROUP BY
`iblock_element_iblock_section`.`NAME`
HAVING
COUNT(`iblock_element`.`NAME`) > 3

Ответ:

Array
(
    [IBLOCK_ELEMENT_IBLOCK_SECTION_NAME] => Породы кошек
    [ELEMENT_COUNT] => 4
    [ELEMENT_LIST] => Абиссинская кошка,Сиамская кошка,Американский бобтейл,Британская короткошерстная
)

Ссылка на другую сущность

Runtime поле может быть не только вычисляемым значением, но и ссылкой на другую сущность. Т.е. в методе getMap() можно не описывать связь, а сформировать ее прямо в запросе. Например, создадим объект Query для сущности IblockTable, свяжем ее с ElementTable и выберем элемент с ID=349:

Bitrix\Main\Loader::includeModule('iblock');
// создаем объект Query, в качестве параметра передаем объект сущности (инфоблок)
$query = new Bitrix\Main\Entity\Query(
    Bitrix\Iblock\IblockTable::getEntity()
);
// регистрируем новое временное поле для исходной сущности
$query->registerRuntimeField( 
    // поле element как ссылка на таблицу b_iblock_element
    'element',
    array(
        // тип — сущность ElementTable
        'data_type' => 'Bitrix\Iblock\ElementTable',
        // this.ID относится к таблице, относительно которой строится запрос, т.е. b_iblock.ID = b_iblock_element.IBLOCK_ID
        'reference' => array('=this.ID' => 'ref.IBLOCK_ID'),
    )
);
// выбираем название элемента, символьный код, краткое описание, кол-во просмотров и название инфоблока
$query->setSelect(array('element.NAME', 'element.CODE', 'element.PREVIEW_TEXT', 'element.SHOW_COUNTER',  'NAME'));
// выбираем только элемент с идентификатором 349
$query->setFilter(array('element.ID' => 349));
// посмотрим, какой запрос был сформирован
echo '<pre>' . $query->getQuery() . '</pre>';
// выполняем запрос
$result = $query->exec();
// выводим результат
while ($row = $result->fetch()) {
    pp($row);
}

Запрос:

SELECT
    `iblock_iblock_element`.`NAME` AS `IBLOCK_IBLOCK_element_NAME`,
    `iblock_iblock_element`.`CODE` AS `IBLOCK_IBLOCK_element_CODE`,
    `iblock_iblock_element`.`PREVIEW_TEXT` AS `IBLOCK_IBLOCK_element_PREVIEW_TEXT`,
    `iblock_iblock_element`.`SHOW_COUNTER` AS `IBLOCK_IBLOCK_element_SHOW_COUNTER`,
    `iblock_iblock`.`NAME` AS `NAME`
  FROM
    `b_iblock` `iblock_iblock` LEFT JOIN `b_iblock_element` `iblock_iblock_element`
    ON `iblock_iblock`.`ID` = `iblock_iblock_element`.`IBLOCK_ID`
  WHERE
    `iblock_iblock_element`.`ID` = 349

Ответ:

Array
(
    [IBLOCK_IBLOCK_element_NAME] => Афганская борзая
    [IBLOCK_IBLOCK_element_CODE] => afganskaya-borzaya
    [IBLOCK_IBLOCK_element_PREVIEW_TEXT] => Изящная красавица с длинной развевающейся на бегу шелковистой шерстью...
    [IBLOCK_IBLOCK_element_SHOW_COUNTER] => 10
    [NAME] => Статьи о домашних животных
)

Пользовательские свойства

Получаем пользовательские свойства элементов инфоблока с идентификатором 5:

Bitrix\Main\Loader::includeModule('iblock');
// создаем объект Query, в качестве параметра передаем объект сущности (свойства)
$query = new Bitrix\Main\Entity\Query(
    Bitrix\Iblock\PropertyTable::getEntity()
);
// выборка
$query->setSelect(array('ID', 'NAME', 'CODE', 'PROPERTY_TYPE'));
// фильтр
$query->setFilter(array('IBLOCK_ID' => 5));
// посмотрим, какой запрос был сформирован
echo '<pre>' . $query->getQuery() . '</pre>';
// выполняем запрос
$result = $query->exec();
// выводим результат
while ($row = $result->fetch()) {
    pp($row);
}

Запрос:

SELECT
    `iblock_property`.`ID` AS `ID`,
    `iblock_property`.`NAME` AS `NAME`,
    `iblock_property`.`CODE` AS `CODE`,
    `iblock_property`.`PROPERTY_TYPE` AS `PROPERTY_TYPE`
  FROM
    `b_iblock_property` `iblock_property`
  WHERE
    `iblock_property`.`IBLOCK_ID` = 5

Ответ:

Array
(
    [ID] => 47
    [NAME] => Автор
    [CODE] => AUTHOR
    [PROPERTY_TYPE] => S
)
Array
(
    [ID] => 48
    [NAME] => Оценка
    [CODE] => RATING
    [PROPERTY_TYPE] => L
)
Array
(
    [ID] => 49
    [NAME] => Галерея
    [CODE] => GALLERY
    [PROPERTY_TYPE] => F
)
Array
(
    [ID] => 51
    [NAME] => Примечание
    [CODE] => NOTE
    [PROPERTY_TYPE] => S
)
Заполните форму уже сегодня!
Для начала сотрудничества необходимо заполнить заявку или заказать обратный звонок. В ответ получите коммерческое предложение, которое будет содержать индивидуальную стратегию с учетом требований и поставленных задач
Работаем по будням с 9:00 до 18:00. Заявки, отправленные в выходные, обрабатываем в первый рабочий день до 12:00.
Спасибо, ваш запрос принят и будет обработан!
Эйч Маркетинг