Построитель запросов 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');
После подключения модуля, нам становится доступен целый набор различных классов и методов для работы с данными инфоблоков. Существуют и другие методы для работы с инфоблоками, и это лишь часть из них. Наиболее часто используемые методы, это получение списков записей:
\Bitrix\Iblock\TypeTable::getEntity();
типы инфоблоков\Bitrix\Iblock\IblockTable::getEntity();
инфоблоки\Bitrix\Iblock\PropertyTable::getEntity();
свойства инфоблоков\Bitrix\Iblock\PropertyEnumerationTable::getEntity();
значения свойств, например списков\Bitrix\Iblock\SectionTable::getEntity();
разделы инфоблоков\Bitrix\Iblock\ElementTable::getEntity();
элементы инфоблоков\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
)