Битрикс. ORM в новом ядре

27.10.2018

Теги: CMSORMWeb-разработкаБазаДанныхБитриксЗапросИнфоблокНовоеЯдро

Реализация ORM в ядре D7 призвана абстрагировать разработчика от механики работы с таблицами на уровне запросов к БД, введя понятие сущности и поля сущности. Сущность — это таблица, поля сущности — столбцы или «ссылки» на другие сущности, а DataManager — система управления данными. Для каждой сущности нужно создать описание, например:

<?php
/*
 * Файл /bitrix/modules/iblock/lib/element.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'),
)

Автоматически сгенерировать класс с описанием любой таблицы можно на странице «Настройки • Производительность • Таблицы», добавив параметр orm=y в адрес:

/bitrix/admin/perfmon_tables.php?lang=ru&orm=y

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

Тренироваться будем на таблицах типов инфоблока, самих инфоблоков, элементов и разделов. Для них описаны сущности TypeTable, IblockTable, ElementTable и SectionTable, их можно посмотреть в исходниках модуля iblock.

Пример № 1

Выбираем элементы инфоблока с идентификатором 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 $query->getQuery();
// выполняем запрос
$result = $query->exec();
// выводим результат
while ($row = $result->fetch()) {
    debug($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] => Афганская борзая
)

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

  • setSelect(), setGroup() — устанавливает массив с именами полей
  • addSelect(), addGroup() — добавляет имя поля
  • getSelect(), getGroup() — возвращает массив с именами полей
  • setFilter() — устанавливает одно- или многомерный массив с описанием фильтра
  • addFilter() — добавляет один параметр фильтра со значением
  • getFilter() — возвращает текущее описание фильтра
  • setOrder() — устанавливает массив с именами полей и порядком сортировки
  • addOrder() — добавляет одно поле с порядком сортировки
  • getOrder() — возвращает текущее описание сортировки
  • setLimit(), setOffset() — устанавливает значение
  • getLimit(), getOffset() — возвращает текущее значение
  • registerRuntimeField() — регистрирует новое временное поле для исходной сущности

Пример № 2

Через сущность «элемент» можно выбирать или ставить условия на поля связанной сущности «инфоблок». Связанная таблица по умолчанию присоединяется с помощью LEFT JOIN. Вспомним reference-поле IBLOCK в описании ElementTable и выберем данные самого инфоблока вместе с элементами инфоблока:

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 $query->getQuery();
// выполняем запрос
$result = $query->exec();
// выводим результат
while ($row = $result->fetch()) {
    debug($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] => Статьи о домашних животных
)

Пример № 3

В запросах можно использовать агрегатные функции 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 $query->getQuery();
// выполняем запрос
$result = $query->exec();
// выводим результат
while ($row = $result->fetch()) {
    debug($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] => Английский бульдог,Далматин,Афганская борзая,Абиссинская кошка,Сиамская 
                         кошка,Американский бобтейл,Британская короткошерстная,Лабрадор,Лайка
)

Пример № 4

Выбираем разделы инфоблока с идентифкатором 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 $query->getQuery();
// выполняем запрос
$result = $query->exec();
// выводим результат
while ($row = $result->fetch()) {
    debug($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] => Абиссинская кошка,Сиамская кошка,Американский бобтейл,Британская короткошерстная
)

Пример № 5

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 $query->getQuery();
// выполняем запрос
$result = $query->exec();
// выводим результат
while ($row = $result->fetch()) {
    debug($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] => Статьи о домашних животных
)

Пример № 6

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

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

// создаем объект Query, в качестве параметра передаем объект сущности (инфоблок)
$query = new Bitrix\Main\Entity\Query(
    Bitrix\Iblock\IblockTable::getEntity()
);
// поле element как ссылка на таблицу b_iblock_element
$query->registerRuntimeField(
    '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'
    )
);
// поле type как ссылка на таблицу b_iblock_type
$query->registerRuntimeField(
    '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 $query->getQuery();
// выполняем запрос
$result = $query->exec();
// выводим результат
while ($row = $result->fetch()) {
    debug($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
)

Пример № 7

Получаем пользовательские свойства элементов инфоблока с идентификатором 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 $query->getQuery();
// выполняем запрос
$result = $query->exec();
// выводим результат
while ($row = $result->fetch()) {
    debug($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
)

Поиск: CMS • DataManager • ElementTable • IblockTable • JOIN • ORM • SQL • SectionTable • Web-разработка • getEntity • query • registerRuntimeField • setFilter • setGroup • setLimit • setOrder • setSelect • table • База данных • Битрикс • Запрос • Инфоблок • Новое ядро • Сущность • Таблица

Каталог оборудования
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Производители
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Функциональные группы
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.