Мы уже умеем выбирать данные из одной таблицы с помощью getList. Но настоящая мощь ORM проявляется, когда нужно в одном запросе получить связанные данные из нескольких таблиц (сделать JOIN). В D7 ORM за это отвечает ReferenceField.
ReferenceField создается «на лету» в runtime-параметре запроса. Он описывает, как одна сущность связана с другой.
Задача: Получить список элементов инфоблока и имена их создателей
В таблице элементов (b_iblock_element) есть поле CREATED_BY, которое хранит ID пользователя. Нам нужно, используя этот ID, «подтянуть» имя и фамилию пользователя из таблицы пользователей (b_user).
Подготовка:
use Bitrix\Main\Loader;
use Bitrix\Iblock\ElementTable;
use Bitrix\Main\Entity; // Не забываем импортировать!
Loader::includeModule('iblock');
Решение с помощью ReferenceField:
$result = ElementTable::getList([
'select' => [
'ID',
'NAME',
'AUTHOR_NAME' => 'USER.NAME',
'AUTHOR_LAST_NAME' => 'USER.LAST_NAME'
],
'filter' => ['=IBLOCK_ID' => 5],
'runtime' => [
// Описываем связь
new Entity\ReferenceField(
'USER', // 1. Псевдоним для связи
\Bitrix\Main\UserTable::class, // 2. Класс сущности, с которой связываемся
['=this.CREATED_BY' => 'ref.ID'] // 3. Условие связи (JOIN ON)
)
]
]);
while ($row = $result->fetch()) {
echo "{$row['NAME']} (Автор: {$row['AUTHOR_NAME']} {$row['AUTHOR_LAST_NAME']})<br>";
}
Разбор ReferenceField:
- ‘USER’: Это псевдоним (алиас), который мы придумываем для нашей связи. Мы будем использовать его в select.
- \Bitrix\Main\UserTable::class: Указываем, к какой ORM-сущности мы «присоединяемся».
- [‘=this.CREATED_BY’ => ‘ref.ID’]: Это условие JOIN.
- this — это наша основная сущность (ElementTable). this.CREATED_BY — это поле в ней.
- ref — это сущность, к которой мы присоединяемся (UserTable). ref.ID — это поле в ней.
- В итоге это транслируется в SQL … ON ElementTable.CREATED_BY = UserTable.ID.
Пример 2: Связь элемента с Highload-блоком
Предположим, у нас есть свойство элемента типа «Привязка к элементам highload-блоков» (UserType hlblock). Нам нужно получить элемент и значение поля из привязанного элемента HL-блока.
- Сначала получаем DataClass для HL-блока (как в статье про HL-блоки). Допустим, он называется MyColorTable.
- Свойство привязки имеет код COLOR. В базе данных оно хранится как PROPERTY_COLOR.
// ... получение $entityDataClass для HL-блока ...
$colorEntityClass = $entityDataClass; // Например, \MyColorTable::class
$result = ElementTable::getList([
'select' => [
'ID',
'NAME',
'COLOR_NAME' => 'COLOR_REF.UF_NAME' // UF_NAME - поле в HL-блоке
],
'filter' => ['=IBLOCK_ID' => 5],
'runtime' => [
new Entity\ReferenceField(
'COLOR_REF',
$colorEntityClass,
['=this.PROPERTY_COLOR.VALUE' => 'ref.ID'] // Условие по значению свойства
)
]
]);
- Обратите внимание на this.PROPERTY_COLOR.VALUE. ORM понимает такой синтаксис и автоматически строит JOIN к таблице значений свойств.
Совмещение с ExpressionField
ReferenceField отлично работает в паре с ExpressionField для выполнения агрегатных функций.
Задача: Посчитать, сколько элементов создал каждый пользователь.
$result = \Bitrix\Main\UserTable::getList([
'select' => ['ID', 'LOGIN', 'ELEMENTS_COUNT'],
'filter' => ['=ACTIVE' => 'Y'],
'runtime' => [
// Сначала описываем связь "один ко многим"
new Entity\ReferenceField(
'ELEMENTS',
\Bitrix\Iblock\ElementTable::class,
['=this.ID' => 'ref.CREATED_BY']
),
// Затем используем эту связь для подсчета
new Entity\ExpressionField(
'ELEMENTS_COUNT',
'COUNT(%s)',
['ELEMENTS.ID']
)
]
]);
while ($row = $result->fetch()) {
echo "Пользователь {$row['LOGIN']} создал {$row['ELEMENTS_COUNT']} элементов.<br>";
}
Вывод:
ReferenceField — это мощнейший инструмент, который открывает доступ к сложным реляционным запросам прямо из PHP, сохраняя код чистым и объектно-ориентированным. Освоив runtime поля, вы сможете решать практически любые задачи по выборке данных, избегая «сырых» SQL-запросов и множественных циклов в коде.
D7 ORM, ReferenceField, JOIN, Bitrix ORM, связи таблиц, runtime, ExpressionField, сложные запросы.