ORM в новом ядре
Реализация ORM в ядре D7 призвана абстрагировать разработчика от механики работы с таблицами на уровне запросов к БД, введя понятие сущности
и поля сущности
.
Cущность
в терминах битрикса, это таблицаПоля сущности
столбцы или ссылки на другие сущностиДатаменеджер
система управления данными
Нужно понимать, работа в ORM возможна только если есть класс описывающий таблицу, с которой нужно работать. Класс может быть сгенерирован и находиться в ядре, а может и отсутствовать. Всё зависит от таблицы данных с которой нужно работать. Работу можно разделить на следующие этапы:
- В папочке
bitrix
пытаемся найти класс, если класс удалось найти значит продолжаем работать с файлом в котором он описан - Если в папочке
bitrix
не удалось найти класс, значит его нужно сгенерировать, как это сделать автоматически написано ниже в статье - Нужно убедиться что система видит класс, если класс не подключен его нужно подключить, есть три варианта:
- через автозагрузку классов
- через Composer
- через модуль
- Пишем необходимые запросы используя
API bitrix
и обрабатываем их
Из чего состоит класс
В классе обязательно должны быть два метода:
getMap()
перечисляются все поля таблицыgetTableName()
указывается название базы данных
Пример класса через который можно работать с таблицей b_iblock
:
/bitrix/modules/iblock/lib/element.php<?
namespace Bitrix\Iblock;
use Bitrix\Main\Localization\Loc,
Bitrix\Main\ORM\Data\DataManager,
Bitrix\Main\ORM\Fields,
Bitrix\Main\Type;
Loc::loadMessages(__FILE__);
/**
* Class IblockTable
*
* Fields:
* <ul>
* <li> ID int mandatory
* <li> TIMESTAMP_X datetime optional default current datetime
* <li> IBLOCK_TYPE_ID string(50) mandatory
* <li> LID string(2) mandatory
* <li> CODE string(50) optional
* <li> API_CODE string(50) optional
* <li> REST_ON bool ('N', 'Y') optional default 'N'
* <li> NAME string(255) mandatory
* <li> ACTIVE bool ('N', 'Y') optional default 'Y'
* <li> SORT int optional default 500
* <li> LIST_PAGE_URL string(255) optional
* <li> DETAIL_PAGE_URL string(255) optional
* <li> SECTION_PAGE_URL string(255) optional
* <li> CANONICAL_PAGE_URL string(255) optional
* <li> PICTURE int optional
* <li> DESCRIPTION text optional
* <li> DESCRIPTION_TYPE enum ('text', 'html') optional default 'text'
* <li> RSS_TTL int optional default 24
* <li> RSS_ACTIVE bool ('N', 'Y') optional default 'Y'
* <li> RSS_FILE_ACTIVE bool ('N', 'Y') optional default 'N'
* <li> RSS_FILE_LIMIT int optional
* <li> RSS_FILE_DAYS int optional
* <li> RSS_YANDEX_ACTIVE bool ('N', 'Y') optional default 'N'
* <li> XML_ID string(255) optional
* <li> TMP_ID string(40) optional
* <li> INDEX_ELEMENT bool ('N', 'Y') optional default 'Y'
* <li> INDEX_SECTION bool ('N', 'Y') optional default 'N'
* <li> WORKFLOW bool ('N', 'Y') optional default 'Y'
* <li> BIZPROC bool ('N', 'Y') optional default 'N'
* <li> SECTION_CHOOSER string(1) optional
* <li> LIST_MODE string(1) optional
* <li> RIGHTS_MODE string(1) optional
* <li> SECTION_PROPERTY string(1) optional
* <li> PROPERTY_INDEX string(1) optional
* <li> VERSION int optional default 1
* <li> LAST_CONV_ELEMENT int optional default 0
* <li> SOCNET_GROUP_ID int optional
* <li> EDIT_FILE_BEFORE string(255) optional
* <li> EDIT_FILE_AFTER string(255) optional
* <li> SECTIONS_NAME string(100) optional
* <li> SECTION_NAME string(100) optional
* <li> ELEMENTS_NAME string(100) optional
* <li> ELEMENT_NAME string(100) optional
* <li> PICTURE reference to {@link \Bitrix\File\FileTable}
* <li> IBLOCK_TYPE_ID reference to {@link \Bitrix\Iblock\IblockTypeTable}
* <li> LID reference to {@link \Bitrix\Lang\LangTable}
* <li> SOCNET_GROUP_ID reference to {@link \Bitrix\Sonet\SonetGroupTable}
* </ul>
*
* @package Bitrix\Iblock
**/
class IblockTable extends DataManager
{
/**
* Returns DB table name for entity.
*
* @return string
*/
public static function getTableName()
{
return 'b_iblock';
}
/**
* Returns entity map definition.
*
* @return array
*/
public static function getMap()
{
return [
new Fields\IntegerField(
'ID',
[
'primary' => true,
'autocomplete' => true,
'title' => Loc::getMessage('IBLOCK_ENTITY_ID_FIELD')
]
),
new Fields\DatetimeField(
'TIMESTAMP_X',
[
'default' => function()
{
return new Type\DateTime();
},
'title' => Loc::getMessage('IBLOCK_ENTITY_TIMESTAMP_X_FIELD')
]
),
new Fields\StringField(
'IBLOCK_TYPE_ID',
[
'required' => true,
'validation' => [__CLASS__, 'validateIblockTypeId'],
'title' => Loc::getMessage('IBLOCK_ENTITY_IBLOCK_TYPE_ID_FIELD')
]
),
new Fields\StringField(
'LID',
[
'required' => true,
'validation' => [__CLASS__, 'validateLid'],
'title' => Loc::getMessage('IBLOCK_ENTITY_LID_FIELD')
]
),
new Fields\StringField(
'CODE',
[
'validation' => [__CLASS__, 'validateCode'],
'title' => Loc::getMessage('IBLOCK_ENTITY_CODE_FIELD')
]
),
new Fields\StringField(
'API_CODE',
[
'validation' => [__CLASS__, 'validateApiCode'],
'title' => Loc::getMessage('IBLOCK_ENTITY_API_CODE_FIELD')
]
),
new Fields\BooleanField(
'REST_ON',
[
'values' => array('N', 'Y'),
'default' => 'N',
'title' => Loc::getMessage('IBLOCK_ENTITY_REST_ON_FIELD')
]
),
new Fields\StringField(
'NAME',
[
'required' => true,
'validation' => [__CLASS__, 'validateName'],
'title' => Loc::getMessage('IBLOCK_ENTITY_NAME_FIELD')
]
),
new Fields\BooleanField(
'ACTIVE',
[
'values' => array('N', 'Y'),
'default' => 'Y',
'title' => Loc::getMessage('IBLOCK_ENTITY_ACTIVE_FIELD')
]
),
new Fields\IntegerField(
'SORT',
[
'default' => 500,
'title' => Loc::getMessage('IBLOCK_ENTITY_SORT_FIELD')
]
),
new Fields\StringField(
'LIST_PAGE_URL',
[
'validation' => [__CLASS__, 'validateListPageUrl'],
'title' => Loc::getMessage('IBLOCK_ENTITY_LIST_PAGE_URL_FIELD')
]
),
new Fields\StringField(
'DETAIL_PAGE_URL',
[
'validation' => [__CLASS__, 'validateDetailPageUrl'],
'title' => Loc::getMessage('IBLOCK_ENTITY_DETAIL_PAGE_URL_FIELD')
]
),
new Fields\StringField(
'SECTION_PAGE_URL',
[
'validation' => [__CLASS__, 'validateSectionPageUrl'],
'title' => Loc::getMessage('IBLOCK_ENTITY_SECTION_PAGE_URL_FIELD')
]
),
new Fields\StringField(
'CANONICAL_PAGE_URL',
[
'validation' => [__CLASS__, 'validateCanonicalPageUrl'],
'title' => Loc::getMessage('IBLOCK_ENTITY_CANONICAL_PAGE_URL_FIELD')
]
),
new Fields\IntegerField(
'PICTURE',
[
'title' => Loc::getMessage('IBLOCK_ENTITY_PICTURE_FIELD')
]
),
new Fields\TextField(
'DESCRIPTION',
[
'title' => Loc::getMessage('IBLOCK_ENTITY_DESCRIPTION_FIELD')
]
),
new Fields\StringField(
'DESCRIPTION_TYPE',
[
'values' => array('text', 'html'),
'default' => 'text',
'title' => Loc::getMessage('IBLOCK_ENTITY_DESCRIPTION_TYPE_FIELD')
]
),
new Fields\IntegerField(
'RSS_TTL',
[
'default' => 24,
'title' => Loc::getMessage('IBLOCK_ENTITY_RSS_TTL_FIELD')
]
),
new Fields\BooleanField(
'RSS_ACTIVE',
[
'values' => array('N', 'Y'),
'default' => 'Y',
'title' => Loc::getMessage('IBLOCK_ENTITY_RSS_ACTIVE_FIELD')
]
),
new Fields\BooleanField(
'RSS_FILE_ACTIVE',
[
'values' => array('N', 'Y'),
'default' => 'N',
'title' => Loc::getMessage('IBLOCK_ENTITY_RSS_FILE_ACTIVE_FIELD')
]
),
new Fields\IntegerField(
'RSS_FILE_LIMIT',
[
'title' => Loc::getMessage('IBLOCK_ENTITY_RSS_FILE_LIMIT_FIELD')
]
),
new Fields\IntegerField(
'RSS_FILE_DAYS',
[
'title' => Loc::getMessage('IBLOCK_ENTITY_RSS_FILE_DAYS_FIELD')
]
),
new Fields\BooleanField(
'RSS_YANDEX_ACTIVE',
[
'values' => array('N', 'Y'),
'default' => 'N',
'title' => Loc::getMessage('IBLOCK_ENTITY_RSS_YANDEX_ACTIVE_FIELD')
]
),
new Fields\StringField(
'XML_ID',
[
'validation' => [__CLASS__, 'validateXmlId'],
'title' => Loc::getMessage('IBLOCK_ENTITY_XML_ID_FIELD')
]
),
new Fields\StringField(
'TMP_ID',
[
'validation' => [__CLASS__, 'validateTmpId'],
'title' => Loc::getMessage('IBLOCK_ENTITY_TMP_ID_FIELD')
]
),
new Fields\BooleanField(
'INDEX_ELEMENT',
[
'values' => array('N', 'Y'),
'default' => 'Y',
'title' => Loc::getMessage('IBLOCK_ENTITY_INDEX_ELEMENT_FIELD')
]
),
new Fields\BooleanField(
'INDEX_SECTION',
[
'values' => array('N', 'Y'),
'default' => 'N',
'title' => Loc::getMessage('IBLOCK_ENTITY_INDEX_SECTION_FIELD')
]
),
new Fields\BooleanField(
'WORKFLOW',
[
'values' => array('N', 'Y'),
'default' => 'Y',
'title' => Loc::getMessage('IBLOCK_ENTITY_WORKFLOW_FIELD')
]
),
new Fields\BooleanField(
'BIZPROC',
[
'values' => array('N', 'Y'),
'default' => 'N',
'title' => Loc::getMessage('IBLOCK_ENTITY_BIZPROC_FIELD')
]
),
new Fields\StringField(
'SECTION_CHOOSER',
[
'validation' => [__CLASS__, 'validateSectionChooser'],
'title' => Loc::getMessage('IBLOCK_ENTITY_SECTION_CHOOSER_FIELD')
]
),
new Fields\StringField(
'LIST_MODE',
[
'validation' => [__CLASS__, 'validateListMode'],
'title' => Loc::getMessage('IBLOCK_ENTITY_LIST_MODE_FIELD')
]
),
new Fields\StringField(
'RIGHTS_MODE',
[
'validation' => [__CLASS__, 'validateRightsMode'],
'title' => Loc::getMessage('IBLOCK_ENTITY_RIGHTS_MODE_FIELD')
]
),
new Fields\StringField(
'SECTION_PROPERTY',
[
'validation' => [__CLASS__, 'validateSectionProperty'],
'title' => Loc::getMessage('IBLOCK_ENTITY_SECTION_PROPERTY_FIELD')
]
),
new Fields\StringField(
'PROPERTY_INDEX',
[
'validation' => [__CLASS__, 'validatePropertyIndex'],
'title' => Loc::getMessage('IBLOCK_ENTITY_PROPERTY_INDEX_FIELD')
]
),
new Fields\IntegerField(
'VERSION',
[
'default' => 1,
'title' => Loc::getMessage('IBLOCK_ENTITY_VERSION_FIELD')
]
),
new Fields\IntegerField(
'LAST_CONV_ELEMENT',
[
'default' => 0,
'title' => Loc::getMessage('IBLOCK_ENTITY_LAST_CONV_ELEMENT_FIELD')
]
),
new Fields\IntegerField(
'SOCNET_GROUP_ID',
[
'title' => Loc::getMessage('IBLOCK_ENTITY_SOCNET_GROUP_ID_FIELD')
]
),
new Fields\StringField(
'EDIT_FILE_BEFORE',
[
'validation' => [__CLASS__, 'validateEditFileBefore'],
'title' => Loc::getMessage('IBLOCK_ENTITY_EDIT_FILE_BEFORE_FIELD')
]
),
new Fields\StringField(
'EDIT_FILE_AFTER',
[
'validation' => [__CLASS__, 'validateEditFileAfter'],
'title' => Loc::getMessage('IBLOCK_ENTITY_EDIT_FILE_AFTER_FIELD')
]
),
new Fields\StringField(
'SECTIONS_NAME',
[
'validation' => [__CLASS__, 'validateSectionsName'],
'title' => Loc::getMessage('IBLOCK_ENTITY_SECTIONS_NAME_FIELD')
]
),
new Fields\StringField(
'SECTION_NAME',
[
'validation' => [__CLASS__, 'validateSectionName'],
'title' => Loc::getMessage('IBLOCK_ENTITY_SECTION_NAME_FIELD')
]
),
new Fields\StringField(
'ELEMENTS_NAME',
[
'validation' => [__CLASS__, 'validateElementsName'],
'title' => Loc::getMessage('IBLOCK_ENTITY_ELEMENTS_NAME_FIELD')
]
),
new Fields\StringField(
'ELEMENT_NAME',
[
'validation' => [__CLASS__, 'validateElementName'],
'title' => Loc::getMessage('IBLOCK_ENTITY_ELEMENT_NAME_FIELD')
]
),
new Fields\Relations\Reference(
'FILE',
'\Bitrix\File\File',
['=this.PICTURE' => 'ref.ID'],
['join_type' => 'LEFT']
),
new Fields\Relations\Reference(
'IBLOCK_TYPE',
'\Bitrix\Iblock\IblockType',
['=this.IBLOCK_TYPE_ID' => 'ref.ID'],
['join_type' => 'LEFT']
),
new Fields\Relations\Reference(
'LANG',
'\Bitrix\Lang\Lang',
['=this.LID' => 'ref.LID'],
['join_type' => 'LEFT']
),
new Fields\Relations\Reference(
'SOCNET_GROUP',
'\Bitrix\Sonet\SonetGroup',
['=this.SOCNET_GROUP_ID' => 'ref.ID'],
['join_type' => 'LEFT']
),
];
}
/**
* Returns validators for IBLOCK_TYPE_ID field.
*
* @return array
*/
public static function validateIblockTypeId()
{
return [
new Fields\Validators\LengthValidator(null, 50),
];
}
/**
* Returns validators for LID field.
*
* @return array
*/
public static function validateLid()
{
return [
new Fields\Validators\LengthValidator(null, 2),
];
}
/**
* Returns validators for CODE field.
*
* @return array
*/
public static function validateCode()
{
return [
new Fields\Validators\LengthValidator(null, 50),
];
}
/**
* Returns validators for API_CODE field.
*
* @return array
*/
public static function validateApiCode()
{
return [
new Fields\Validators\LengthValidator(null, 50),
];
}
/**
* Returns validators for NAME field.
*
* @return array
*/
public static function validateName()
{
return [
new Fields\Validators\LengthValidator(null, 255),
];
}
/**
* Returns validators for LIST_PAGE_URL field.
*
* @return array
*/
public static function validateListPageUrl()
{
return [
new Fields\Validators\LengthValidator(null, 255),
];
}
/**
* Returns validators for DETAIL_PAGE_URL field.
*
* @return array
*/
public static function validateDetailPageUrl()
{
return [
new Fields\Validators\LengthValidator(null, 255),
];
}
/**
* Returns validators for SECTION_PAGE_URL field.
*
* @return array
*/
public static function validateSectionPageUrl()
{
return [
new Fields\Validators\LengthValidator(null, 255),
];
}
/**
* Returns validators for CANONICAL_PAGE_URL field.
*
* @return array
*/
public static function validateCanonicalPageUrl()
{
return [
new Fields\Validators\LengthValidator(null, 255),
];
}
/**
* Returns validators for XML_ID field.
*
* @return array
*/
public static function validateXmlId()
{
return [
new Fields\Validators\LengthValidator(null, 255),
];
}
/**
* Returns validators for TMP_ID field.
*
* @return array
*/
public static function validateTmpId()
{
return [
new Fields\Validators\LengthValidator(null, 40),
];
}
/**
* Returns validators for SECTION_CHOOSER field.
*
* @return array
*/
public static function validateSectionChooser()
{
return [
new Fields\Validators\LengthValidator(null, 1),
];
}
/**
* Returns validators for LIST_MODE field.
*
* @return array
*/
public static function validateListMode()
{
return [
new Fields\Validators\LengthValidator(null, 1),
];
}
/**
* Returns validators for RIGHTS_MODE field.
*
* @return array
*/
public static function validateRightsMode()
{
return [
new Fields\Validators\LengthValidator(null, 1),
];
}
/**
* Returns validators for SECTION_PROPERTY field.
*
* @return array
*/
public static function validateSectionProperty()
{
return [
new Fields\Validators\LengthValidator(null, 1),
];
}
/**
* Returns validators for PROPERTY_INDEX field.
*
* @return array
*/
public static function validatePropertyIndex()
{
return [
new Fields\Validators\LengthValidator(null, 1),
];
}
/**
* Returns validators for EDIT_FILE_BEFORE field.
*
* @return array
*/
public static function validateEditFileBefore()
{
return [
new Fields\Validators\LengthValidator(null, 255),
];
}
/**
* Returns validators for EDIT_FILE_AFTER field.
*
* @return array
*/
public static function validateEditFileAfter()
{
return [
new Fields\Validators\LengthValidator(null, 255),
];
}
/**
* Returns validators for SECTIONS_NAME field.
*
* @return array
*/
public static function validateSectionsName()
{
return [
new Fields\Validators\LengthValidator(null, 100),
];
}
/**
* Returns validators for SECTION_NAME field.
*
* @return array
*/
public static function validateSectionName()
{
return [
new Fields\Validators\LengthValidator(null, 100),
];
}
/**
* Returns validators for ELEMENTS_NAME field.
*
* @return array
*/
public static function validateElementsName()
{
return [
new Fields\Validators\LengthValidator(null, 100),
];
}
/**
* Returns validators for ELEMENT_NAME field.
*
* @return array
*/
public static function validateElementName()
{
return [
new Fields\Validators\LengthValidator(null, 100),
];
}
}
Автоматическая генерация класса
Для использования генератора ORM классов перейдите на страницу Настройки -> Настройки продукта -> Настройки модулей -> Монитор производительности
модуль Монитор производительности
должен быть установлен. На вкладке Генератор таблетов
отметьте поле Разрешить генерацию таблетов для ORM
.
Автоматически сгенерировать класс с описанием любой таблицы можно на странице Настройки -> Производительность -> Таблицы
, в меню действий доступен пункт ORM
Операции с сущностями
Для работы с таблицей стандартными методами D7 доступны следующие методы:
getList
выполняет запрос и возвращает отобранные по параметрам запроса данныеadd
добавляет новый элементupdate
обновляет строку в таблицеdelete
удаляет строку в таблице сущности по первичному ключу
GetList
Пример запроса:
// подключаем модуль, через модуль подключается класс IblockTable
\Bitrix\Main\Loader::IncludeModule("iblock");
// делаем запрос на выборку данных getList
$res = \Bitrix\Iblock\IblockTable::getList(array(
'select' => array('ID')
));
// перебираем массив и выводим на экран
while ($arr = $res->fetch()) {
pp($arr);
}
Операторы:
IblockTable::getList(array(
// имена полей, которые необходимо получить
'select' => array('ISBN', 'TITLE', 'PUBLISH_DATE'),
// описание фильтра для WHERE и HAVING
'filter' => array('=ID' => 1),
// явное указание полей, по которым нужно группировать результат
'group' => array('PUBLISH_DATE'),
// параметры сортировки
'order' => array('PUBLISH_DATE' => 'DESC', 'TITLE' => 'ASC'),
// количество записей
'limit' => 10,
// смещение для limit
'offset' => 80,
// динамически определенные поля
'runtime' => 'runtime' => array(new Entity\ExpressionField('CNT', 'COUNT(*)'))
));
select
array(
'select' => array('ISBN', 'TITLE', 'PUBLISH_DATE')
)
// SELECT ISBN, TITLE, PUBLISH_DATE FROM my_book
array(
'select' => array('ISBN', 'TITLE', 'PUBLICATION' => 'PUBLISH_DATE')
)
// SELECT ISBN, TITLE, PUBLISH_DATE AS PUBLICATION FROM my_book
array(
'select' => array('*')
)
// SELECT *
filter
array(
'filter' => array('=ID' => 1)
)
// WHERE ID = 1
array(
'filter' => array('%=TITLE' => 'Patterns%')
)
// WHERE TITLE LIKE 'Patterns%'
Многоуровневые массивы со склейкой выражений AND/OR
:
array(
'filter' => array(
'=ID' => 1,
'=ISBN' => '9780321127426'
)
)
// WHERE ID = 1 AND ISBN = '9780321127426'
array(
'filter' => array(
// по умолчанию элементы склеиваются через AND
//'LOGIC' => 'AND',
'LOGIC' => 'OR',
array(
'=ID' => 1,
'=ISBN' => '9780321127426'
),
array(
'=ID' => 2,
'=ISBN' => '9781449314286'
)
)
)
// WHERE (ID=1 AND ISBN='9780321127426') OR (ID=2 AND ISBN='9781449314286')
Полный список операторов сравнения, которые можно использовать в filter
:
'filter' => array('=ID' => 1)
=
равно (работает и с массивами)%
подстрока>
больше<
меньше@ IN (EXPR)
в качестве значения передается массив или объект!@ NOT IN (EXPR)
в качестве значения передается массив или объект!=
не равно!%
не подстрока><
между, в качестве значения передается массив array(MIN, MAX)>=
больше или равно<=
меньше или равно=%
LIKE%=
LIKE==
булевое выражение для ExpressionField (например, для EXISTS() или NOT EXISTS())!><
не между, в качестве значения передается массив array(MIN, MAX)!=%
NOT LIKE!%=
NOT LIKE'==ID' => null
условие, что поле ID равно NULL (в sql-запросе будет преобразовано в ID IS NULL)'!==NAME' => null
условие, что поле NAME не равно NULL (в sql-запросе будет преобразовано в NAME IS NOT NULL)
group
В параметре group перечисляются поля для группировки:
array(
'group' => array('PUBLISH_DATE')
)
order
Параметр позволяет указать порядок сортировки:
array(
'order' => array('PUBLISH_DATE' => 'DESC', 'TITLE' => 'ASC')
)
array(
'order' => array('ID') // направление по умолчанию - ASC
)
offset/limit
Параметры помогут ограничить количество выбираемых записей или реализовать постраничную выборку:
array(
'order' => array('ID' => 'DESC')
'limit' => 10
)
// 10 последних записей
array(
'order' => array('ID')
'limit' => 20,
'offset' => 80
)
// 5-я страница с записями, по 20 на страницу
Add
Метод для добавления записи принимает на вход массив со значениями, где ключи - имена полей сущности:
// подключаем модуль, через модуль подключается класс IblockTable
\Bitrix\Main\Loader::IncludeModule("iblock");
// делаем запрос на вставку данных add
$res = \Bitrix\Iblock\IblockTable::add(array(
'NAME' => 'Тест'
));
Update
Обновление записи происходит похожим образом, только к массиву значений в параметрах добавляется значение первичного ключа:
// подключаем модуль, через модуль подключается класс IblockTable
\Bitrix\Main\Loader::IncludeModule("iblock");
// делаем запрос на тзменение поля NAME в записи с ID 1
$res = \Bitrix\Iblock\IblockTable::update(1, array(
'NAME' => 'Тест'
));
Delete
Для удаления записи нужен только первичный ключ:
// подключаем модуль, через модуль подключается класс IblockTable
\Bitrix\Main\Loader::IncludeModule("iblock");
// делаем запрос на удаление записи с ID 1
$res = \Bitrix\Iblock\IblockTable::delete(1);
Вывод SQL запроса на экран
Чтобы понять причину «неправильно» работающей выборки, нужно посмотреть какой sql-запрос формируетя. Рассмотрим на примере выборок из ORM ядра D7 в битриксе:
// подключаем класс для вывода SQL запроса
\Bitrix\Main\Application::getConnection()->startTracker();
// подключаем модуль, через модуль подключается класс IblockTable
\Bitrix\Main\Loader::IncludeModule("iblock");
// делаем запрос на выборку данных getList
$res = \Bitrix\Iblock\IblockTable::getList(array(
'select' => array('ID')
));
// перебираем массив и выводим на экран
while ($arr = $res->fetch()) {
pp($arr);
}
// выводим на экран SQL запрос
echo '<pre>', $res->getTrackerQuery()->getSql(), '</pre>';