Выборка данных
Для выборки данных с фильтрацией, группировкой и сортировкой в Bitrix Framework используются метод getList и объект Entity\Query.
Метод getList
Метод getList универсален для всех сущностей и работает по общим правилам. Его невозможно изменить, но он дает гибкость для выполнения запросов.
Рассмотрим применение метода на примере сущности BookTable. Метод принимает параметры:
$result = BookTable::getList([
'select' => ['ISBN', 'TITLE', 'PUBLISH_DATE', 'CNT'], // поля, которые нужно получить
'filter' => ['=ID' => 1], // условия фильтрации
'group' => ['PUBLISH_DATE'], // поля для группировки
'order' => ['PUBLISH_DATE' => 'DESC'], // параметры сортировки
'limit' => 10, // количество записей
'offset' => 0, // смещение для limit
'runtime' => [ // динамически определенные поля
new ORM\Fields\ExpressionField('CNT', 'COUNT(*)')
],
'count_total' => true // список всех элементов без постраничного вывода
]);
-
select— выбирает поля для выборки -
filter— задает условия фильтрации, аналогичноWHEREв SQL -
group— группирует данные по указанным полям -
order— сортирует данные по указанным полям -
limitиoffset— ограничивают количество записей и задают смещение -
runtime— добавляет временные поля, которые вычисляются во время выполнения запроса -
count_total— получает список всех элементов без постраничного вывода
Получение данных из результата
Метод getList возвращает объект DB\Result. Данные можно получить с помощью методов fetch() или fetchAll().
// Получение данных построчно
$rows = [];
$result = BookTable::getList([$parameters]);
while ($row = $result->fetch())
{
$rows[] = $row;
}
// Получение всех данных сразу
$rows = BookTable::getList($parameters)->fetchAll();
Форматирование данных
Для изменения формата данных после выборки можно использовать метод fetchDataModification(). Например, изменить формат даты:
class BookTable extends \Bitrix\Main\Entity\DataManager
{
public static function fetchDataModification(): array
{
return [
function ($data)
{
if (isset($data['PUBLISH_DATE']))
{
$data['PUBLISH_DATE'] = date('d.m.Y', strtotime($data['PUBLISH_DATE']));
}
return $data;
}
];
}
}
// Использование
$result = BookTable::getList($parameters);
$rows = $result->fetchAll();
Параметры метода getList
Select
Параметр select задается как массив с именами полей сущности.
BookTable::getList([
'select' => ['ISBN', 'TITLE', 'PUBLISH_DATE']
]);
// SELECT ISBN, TITLE, PUBLISH_DATE FROM my_book
Чтобы изменить названия полей, используйте алиасы. Алиасы позволяют переименовывать поля в результирующем наборе данных для удобства. Например, чтобы поле PUBLISH_DATE отображалось как PUBLICATION:
BookTable::getList([
'select' => ['ISBN', 'TITLE', 'PUBLICATION' => 'PUBLISH_DATE']
]);
// SELECT ISBN, TITLE, PUBLISH_DATE AS PUBLICATION FROM my_book
Для выбора всех полей используйте символ *.
BookTable::getList([
'select' => ['*']
]);
При этом выберутся только скалярные поля ScalarField. Скалярные поля — это обычные поля таблицы. Вычисляемые поля ExpressionField и связи с другими сущностями нужно указывать в запросе. Они создаются на основе выражений и не являются частью таблицы по умолчанию.
Filter
Параметр filter задает условия для выборки данных в виде ассоциативного массива. Ключи массива это условия, а значения — параметры для поиска.
// WHERE ID = 1
BookTable::getList([
'filter' => ['=ID' => 1]
]);
// WHERE TITLE LIKE 'Patterns%'
BookTable::getList([
'filter' => ['%=TITLE' => 'Patterns%']
]);
Фильтр может быть многоуровневым с использованием AND или OR, что позволяет комбинировать условия:
// WHERE ID = 1 AND ISBN = '9780321127426'
BookTable::getList([
'filter' => [
'=ID' => 1,
'=ISBN' => '9780321127426'
]
]);
// WHERE (ID=1 AND ISBN='9780321127426') OR (ID=2 AND ISBN='9781449314286')
BookTable::getList([
'filter' => [
'LOGIC' => 'OR',
[
'=ID' => 1,
'=ISBN' => '9780321127426'
],
[
'=ID' => 2,
'=ISBN' => '9781449314286'
]
]
]);
Операторы сравнения
Операторы сравнения позволяют задавать условия фильтрации.
-
=— равно, работает и с массивами -
%— подстрока -
>— больше -
<— меньше -
@— IN (EXPR), принимает массив или объектDB\SqlExpression -
!@— NOT IN (EXPR), принимает массив или объектDB\SqlExpression -
!=— не равно -
!%— не подстрока -
><— между, принимает массивarray(MIN, MAX) -
>=— больше или равно -
<=— меньше или равно -
=%— ищет строки, которые начинаются с указанного значения. Аналог оператора LIKE в SQL -
%=— ищет строки, которые заканчиваются указанным значением. Аналог оператора LIKE в SQL
Префиксы
Префиксы %= и =% эквивалентны и используются для поиска подстрок.
-
'%=NAME' => 'тест'— аналог LIKE, не подстрока -
'%=NAME' => 'тест%'— содержит «тест» в начале -
'%=NAME' => '%тест'— содержит «тест» в конце -
'%=NAME' => '%тест%'— содержит подстроку «тест»
Последний вариант отличается от '%NAME' => 'тест' итоговым SQL-запросом.
-
==— булево выражение для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
Если не указан оператор =, по умолчанию используется LIKE — поиск строки, которая содержит указанное значение в любом месте.
Для полей типа int
Если передать массив значений в фильтр для поля типа int, фреймворк автоматически преобразует это в SQL-запрос с использованием IN(). Оператор IN проверит, входит ли значение поля в указанный список.
$result = BookTable::getList([
'filter' => [
'ID' => [1, 2, 3] // Массив значений для фильтрации
]
]);
// Bitrix ORM преобразует этот запрос в SQL:
// SELECT * FROM book_table WHERE ID IN (1, 2, 3);
Group
Параметр group задает поля для группировки. Это позволяет объединять записи с одинаковыми значениями в указанных полях:
BookTable::getList([
'group' => ['PUBLISH_DATE']
]);
Чаще всего группировку указывать не нужно — система сделает это автоматически.
Order
Параметр order задает порядок сортировки.
BookTable::getList([
'order' => ['PUBLISH_DATE' => 'DESC', 'TITLE' => 'ASC']
]);
BookTable::getList([
'order' => ['ID'] // направление по умолчанию — ASC
]);
-
ASC— по возрастанию -
DESC— по убыванию
Limit и Offset
Параметры limit и offset ограничивают количество записей и создают постраничную выборку.
// 10 последних записей
BookTable::getList([
'order' => ['ID' => 'DESC'],
'limit' => 10
]);
// Получить записи для пятой страницы, если на каждой странице отображается по 20 записей
BookTable::getList([
'order' => ['ID'],
'limit' => 20,
'offset' => 80
]);
Runtime
Параметр runtime добавляет временные поля, которые вычисляются во время выполнения запроса.
Пример подсчета записей
Для подсчета количества записей используйте ExpressionField:
BookTable::getList([
'select' => ['CNT'],
'runtime' => [
new ORM\Fields\ExpressionField('CNT', 'COUNT(*)')
]
]);
// посчитать общее количество записей в таблице
// SELECT COUNT(*) AS CNT FROM my_book
Здесь вычисляемое поле реализует SQL-выражение с функцией COUNT.
Использование в фильтрах
После того, как добавили вычисляемое поле, его можно использовать в фильтрах:
BookTable::getList([
'select' => ['PUBLISH_DATE'],
'filter' => ['>CNT' => 5],
'runtime' => [
new ORM\Fields\ExpressionField('CNT', 'COUNT(*)')
]
]);
// посчитать общее количество записей в таблице
// выбрать те дни, когда выпущено более 5 книг
// SELECT PUBLISH_DATE, COUNT(*) AS CNT FROM my_book GROUP BY PUBLISH_DATE HAVING COUNT(*) > 5
Система автоматически группирует по PUBLISH_DATE.
Регистрация других типов полей
В runtime можно регистрировать не только Expression поля, но и другие типы. Механизм runtime добавляет поле к сущности, как если бы оно было описано в getMap. Однако такое поле доступно только в рамках одного запроса и требует повторной регистрации в следующем вызове getList.
Вычисляемые поля без runtime
Если вычисляемое поле нужно только в select, runtime можно не использовать. Система поддерживает вложенные выражения, которые разворачиваются в финальном SQL.
BookTable::getList([
'select' => [
new ORM\Fields\ExpressionField('MAX_AGE', 'MAX(%s)', ['AGE_DAYS'])
]
]);
// SELECT MAX(DATEDIFF(NOW(), PUBLISH_DATE)) AS MAX_AGE FROM my_book
-
Создается вычисляемое поле
MAX_AGEчерезExpressionField -
Используется вложенное выражение
MAX(%s), где%sзаменяется наAGE_DAYS -
AGE_DAYSавтоматически разворачивается вDATEDIFF(NOW(), PUBLISH_DATE)
Вложенные выражения разворачиваются последовательно.
Count_total
Чтобы получить список всех элементов без постраничного вывода, используйте параметр count_total со значением true:
$res = MyTable::getList([
/* ваши параметры запроса */
'count_total' => true,
]);
Это позволяет получить весь список в один запрос.
$res->getCount(); // все элементы без пагинации
Кеширование выборки
Кеширование выборки позволяет сохранять результаты запросов для повторного использования. По умолчанию кеширование отключено.
Для включения кеширования используйте ключ cache в методе getList:
$res = \Bitrix\Main\GroupTable::getList([
'filter' => ['=ID' => 1],
'cache' => ['ttl' => 3600] // Время жизни кеша в секундах
]);
То же самое можно сделать с помощью объекта Query:
$query = \Bitrix\Main\GroupTable::query();
$query->setSelect(['*']);
$query->setFilter(['=ID' => 1]);
$query->setCacheTtl(150);
$res = $query->exec();
Особенности кеширования
-
Если данные найдены в кеше, в результате кешированной выборки вернется объект
ArrayResult. -
По умолчанию выборки с
JOINне кешируются. Чтобы включить кеширование сJOINиспользуйте ключcache_joins.
"cache" => ["ttl" => 3600, "cache_joins" => true];
// или
$query->cacheJoins(true);
Сброс кеша
Кеш автоматически сбрасывается при вызове методов add, update, delete. Для принудительного сброса кеша используйте метод cleanCache().
/* Пример для таблицы пользователей */
\Bitrix\Main\UserTable::getEntity()->cleanCache();
Настройки администратора
Администратор проекта может запретить кеширование или изменить время жизни кеша TTL.
Короткие вызовы
Для упрощения работы с данными, помимо метода getList, доступны более короткие методы:
-
getById($id)— выборка по первичному ключу. -
getByPrimary($primary, array $parameters)— выборка по первичному ключу с дополнительными параметрами.
В обоих методах id можно передавать как число или массив. Массив используется, если есть несколько первичных полей. Если элемент массива не является первичным ключом, возникнет ошибка.
BookTable::getById(1);
BookTable::getByPrimary(['ID' => 1]);
// Эти вызовы аналогичны следующему вызову getList:
BookTable::getList([
'filter' => ['=ID' => 1]
]);
getRowById($id)— выборка по первичному ключу, возвращает массив данных.
$row = BookTable::getRowById($id);
// Аналогичный результат можно получить так:
$result = BookTable::getById($id);
$row = $result->fetch();
// Или так:
$result = BookTable::getList([
'filter' => ['=ID' => $id]
]);
$row = $result->fetch();
getRow(array $parameters)— выборка по другим параметрам, возвращает одну запись.
$row = BookTable::getRow([
'filter' => ['%=TITLE' => 'Patterns%'],
'order' => ['ID']
]);
// Аналогичный результат можно получить так:
$result = BookTable::getList([
'filter' => ['%=TITLE' => 'Patterns%'],
'order' => ['ID'],
'limit' => 1
]);
$row = $result->fetch();
Предустановленные выборки
Предустановленные выборки позволяют заранее задавать параметры для выборки данных: фильтры, сортировки или выбор полей. Их можно разделить на два уровня:
-
Глобальная область данных — настройки применяются ко всем запросам, связанным с определенной сущностью.
-
Локальная область данных — настройки применяются только к конкретному запросу и могут быть изменены.
Глобальная область данных
Одну таблицу можно описать несколькими сущностями, разделив записи на сегменты. Метод setDefaultScope выполняется при каждом запросе, задавая фильтры и другие параметры.
class Element4Table extends \Bitrix\Iblock\ElementTable
{
public static function getTableName()
{
return 'b_iblock_element';
}
public static function setDefaultScope(Query $query)
{
$query->where("IBLOCK_ID", 4);
}
}
class Element5Table extends \Bitrix\Iblock\ElementTable
{
public static function getTableName()
{
return 'b_iblock_element';
}
public static function setDefaultScope(Query $query)
{
$query->where("IBLOCK_ID", 5);
}
}
-
getTableNameуказывает имя общей таблицыb_iblock_element. Ее используют обе сущности. -
setDefaultScopeопределяет дополнительные условия для каждого запроса:-
в классе
Element4Tableфильтруются записи сIBLOCK_ID = 4. -
в классе
Element5Tableфильтруются записи сIBLOCK_ID = 5.
-
Хотя обе сущности работают с одной таблицей, они оперируют разными наборами данных благодаря настроенным фильтрам.
Локальная область данных
На пользовательском уровне можно задавать предустановленные выборки с помощью методов with*, аналога setDefaultScope.
class UserTable
{
// Метод withActive добавляет условие фильтрации по полю ACTIVE
public static function withActive(Query $query)
{
$query->where('ACTIVE', true); // Фильтр ACTIVE = true
}
}
// Использование метода withActive
$activeUsers = UserTable::query()
->withActive() // Применяем фильтр для активных пользователей
->fetchCollection(); // Выполняем запрос и получаем коллекцию
// WHERE `ACTIVE`='Y'
-
Метод
withActiveдобавляет условиеWHERE ACTIVE = trueк запросу. -
Запрос выполняется только для активных пользователей.
Метод принимает объект Bitrix\Main\ORM\Query\Query, позволяя задавать фильтры и другие параметры. Можно добавить свои аргументы:
class UserTable
{
// Метод withActive принимает значение для фильтрации и добавляет поле LOGIN в выборку
public static function withActive(Query $query, $value)
{
$query
->addSelect('LOGIN') // Добавляем поле LOGIN в выборку
->where('ACTIVE', $value); // Фильтр ACTIVE = $value
}
}
// Использование метода withActive
$activeUsers = UserTable::query()
->withActive(false) // Применяем фильтр для неактивных пользователей
->fetchCollection(); // Выполняем запрос и получаем коллекцию
// SELECT `LOGIN` ... WHERE `ACTIVE`='N'
-
Метод
withActiveдобавляет полеLOGINв выборку с помощьюaddSelect. -
Фильтр
WHERE ACTIVE = $valueприменяется в зависимости от переданного значения. В примере передаемfalse. -
Запрос выполняется для неактивных пользователей
ACTIVE = 'N'.
Выбор данных из хранимых процедур вместо таблиц
ORM может использоваться для сложных запросов, таких как выборка данных из хранимых процедур в базе данных MSSQL. Хранимые процедуры — это программы, заранее сохраненные на сервере базы данных, которые могут выполнять сложные операции и возвращать результаты.
Проблема с именами хранимых процедур
При попытке указать хранимую процедуру вместо имени таблицы в методе getTableName, возникает ошибка:
public static function getTableName()
{
// return "foo_table_name"
return "foo_table_procedure()";
}
/* ошибка */
// MS Sql query error: Invalid object name 'foo_table_procedure()'. (400)
// SELECT [base].[bar] AS [BAR], [base].[baz] AS [BAZ], FROM [foo_table_procedure()] [base]
Система автоматически добавляет квадратные скобки вокруг имени хранимой процедуры, что делает запрос некорректным.
Решение проблемы
Чтобы использовать хранимые процедуры, нужно создать собственный класс SqlHelper:
-
Установите расширение
mssqlна сервере -
Создайте новый класс, унаследованный от
Bitrix\Main\DB\SqlHelperclass MssqlSqlHelper extends \Bitrix\Main\DB\SqlHelper { public function quote($identifier) { if (self::isKnownFunctionCall($identifier)) { return $identifier; } else { return parent::quote($identifier); } } } -
Реализуйте метод
isKnownFunctionCall, который проверяет, является ли$identifierвызовом хранимой процедуры, например,"foo_table_procedure()".
Как работает решение
-
Если
$identifierявляется вызовом хранимой процедуры, квадратные скобки не добавляются -
Для обычных таблиц сохраняется стандартное поведение с экранированием
Выборки в отношениях 1:N и N:M
При работе с отношениями 1:N и N:M могут возникнуть две основные проблемы.
Некорректная работа LIMIT
При использовании метода setLimit в запросах с отношениями 1:N и N:M может возникнуть неожиданное поведение. Ограничение LIMIT применяется на уровне SQL-запроса и ограничивает общее количество строк в результате, включая связанные записи. Например, если у элемента есть несколько значений для множественного свойства, каждое из этих значений будет считаться отдельной строкой. В результате вместо ожидаемых 5 элементов может быть выбрано меньше, либо один элемент с неполными значениями свойств.
$query = BookTable::query()
->addSelect('NAME')
->addSelect('AUTHORS')
->setLimit(5);
$books = $query->fetchCollection();
// SQL-запрос:
/*
SELECT ... FROM `b_books`
LEFT JOIN `b_books_authors` ...
LIMIT 5
*/
Декартово произведение при выборке нескольких отношений
Декартово произведение возникает, когда каждая строка одной таблицы соединяется с каждой строкой другой таблицы, что приводит к резкому увеличению количества строк в результате. При выборке нескольких связанных полей в одном запросе результат может значительно увеличиваться в размере.
$query = BookTable::query()
->addSelect('NAME')
->addSelect('AUTHORS')
->addSelect('CATEGORIES')
->addSelect('TAGS');
$books = $query->fetchCollection();
// SQL-запрос
/*
SELECT ... FROM `b_books`
LEFT JOIN `b_books_authors` ... // 15 значений
LEFT JOIN `b_books_categories` ... // 7 значений
LEFT JOIN `b_books_tags` ... // 11 значений
*/
-
Каждое добавленное свойство создает LEFT JOIN
-
Результат представляет декартово произведение всех связанных записей.
-
Если у свойств 15, 7 и 11 значений соответственно, будет выбрано 15 * 7 * 11 = 1155 строк вместо ожидаемых 15 + 7 + 11 = 33 строки.
-
Это может привести к нехватке памяти и снижению производительности при большом количестве свойств или значений.
Решение проблем
Для решения этих проблем используется класс Bitrix\Main\ORM\Query\QueryHelper с методом decompose.
public static function decompose(Query $query, $fairLimit = true, $separateRelations = true)
Метод имеет два основных параметра:
-
fairLimit. По умолчанию —true.Разбивает запрос на два этапа:-
Сначала выбираются
IDосновных записей с учетомLIMITиOFFSET -
Затем для найденных
IDвыбираются все отношения
-
-
separateRelations. По умолчанию —true.Выбирает каждое отношение 1:N или N:M отдельным запросом, что исключает возникновение декартова произведения
В результате возвращается коллекция объектов с объединенными данными. Сортировка применяется только к основным записям. Порядок объектов отношений внутри основных обычно не имеет значения
Фильтр ORM
Фильтр ORM позволяет задавать условия для выборки данных из базы.
Одиночные условия
-
Простой запрос
Для выборки данных по конкретному условию используйте метод
where. Например, чтобы выбрать пользователя сID = 1:\Bitrix\Main\UserTable::query() ->where("ID", 1) ->exec(); // WHERE `main_user`.`ID` = 1 -
Использование других операторов
Вы можете использовать различные операторы для сравнения. Например, чтобы выбрать пользователей с ID меньше 10:
\Bitrix\Main\UserTable::query() ->where("ID", "<", 10) ->exec(); // WHERE `main_user`.`ID` < 10В
\Bitrix\Main\ORM\Query\Filter\Operator::$operatorsдоступны следующие операторы:=,<>,!=,<,<=,>,>=,in,between,like,exists. -
Дополнительные методы
-
whereNull($column)— является ли значениеNULL. -
whereIn($column, $values|Query|SqlExpression)— входит ли значение в указанный список -
whereBetween($column, $valueMin, $valueMax)— находится ли значение в заданном диапазоне. -
whereLike($column, $value)— соответствует ли значение шаблону. -
whereExists($query|SqlExpression)— проверяет существование подзапроса.
Пример. Выбрать записи, где
IDравноNULL:\Bitrix\Main\UserTable::query() ->whereNull("ID") ->exec(); // WHERE `main_user`.`ID` IS NULL -
-
Методы whereNot*
Для каждого метода
where*существует аналогwhereNot*, например,whereNotNull,whereNotIn, и так далее.Пример. Выбрать записи, где
IDравноNOT NULL:\Bitrix\Main\UserTable::query() ->whereNotNull("ID") ->exec(); // WHERE `main_user`.`ID` IS NOT NULL -
Произвольные выражения с whereExpr
Для сложных условий используйте
whereExpr, который позволяет задавать произвольные SQL-выражения:\Bitrix\Main\UserTable::query() ->whereExpr('JSON_CONTAINS(%s, 4)', ['SOME_JSON_FIELD']) ->exec(); // WHERE JSON_CONTAINS(`main_user`.`SOME_JSON_FIELD`, 4)Аргументы выражения аналогичны конструктору поля
ExpressionFieldи подставляются черезsprintf. -
Сравнение с другим полем
Метод
whereColumnупрощает сравнение полей.\Bitrix\Main\UserTable::query() ->whereColumn('NAME', 'LOGIN') ->exec(); // WHERE `main_user`.`NAME` = `main_user`.`LOGIN`Этот метод аналогичен вызову:
\Bitrix\Main\UserTable::query() ->where('NAME', new Query\Filter\Expression\Column('LOGIN')) ->exec(); // WHERE `main_user`.`NAME` = `main_user`.`LOGIN`Метод
whereInпозволяет гибко использовать колонки в фильтре.\Bitrix\Main\UserTable::query() ->whereIn('LOGIN', [ new Column('NAME'), new Column('LAST_NAME') ]) ->exec(); // WHERE `main_user`.`LOGIN` IN (`main_user`.`NAME`, `main_user`.`LAST_NAME`)Колонки воспринимаются как поля сущностей, а не произвольные SQL-выражения.
Множественные условия
Используйте метод where несколько раз, чтобы добавлять несколько условий в запрос:
\Bitrix\Main\UserTable::query()
->where('ID', '>', 1)
->where('ACTIVE', true)
->whereNotNull('PERSONAL_BIRTHDAY')
->whereLike('NAME', 'A%')
->exec();
// WHERE `main_user`.`ID` > 1 AND `main_user`.`ACTIVE` = 'Y' AND `main_user`.`PERSONAL_BIRTHDAY` IS NOT NULL AND `main_user`.`NAME` LIKE 'A%'
Для boolean полей со значениями Y/N, 1/0 и так далее, можно использовать true и false в условиях.
Несколько условий можно указать в одном вызове where:
\Bitrix\Main\UserTable::query()
->where([
['ID', '>', 1],
['ACTIVE', true],
['PERSONAL_BIRTHDAY', '<>', null],
['NAME', 'like', 'A%']
])
->exec();
// WHERE `main_user`.`ID` > 1 AND `main_user`.`ACTIVE` = 'Y' AND `main_user`.`PERSONAL_BIRTHDAY` IS NOT NULL AND `main_user`.`NAME` LIKE 'A%'
OR и вложенные фильтры
Для хранения условий фильтра используется контейнер \Bitrix\Main\Entity\Query\Filter\ConditionTree. Он позволяет добавлять другие экземпляры ConditionTree для создания вложенных условий.
Пример простого фильтра
\Bitrix\Main\UserTable::query()
->where([
['ID', '>', 1],
['ACTIVE', true]
])
->exec();
// WHERE `main_user`.`ID` > 1 AND `main_user`.`ACTIVE` = 'Y'
Вложенные фильтры
Вы можете использовать вложенные фильтры для более сложных условий:
use \Bitrix\Main\ORM\Query\Query;
\Bitrix\Main\UserTable::query()
->where(Query::filter()->where([
["ID", '>', 1],
['ACTIVE', true]
]))->exec();
// WHERE `main_user`.`ID` > 1 AND `main_user`.`ACTIVE` = 'Y'
Использование логики OR
Для объединения условий с логикой OR:
use \Bitrix\Main\ORM\Query\Query;
\Bitrix\Main\UserTable::query()
->where('ACTIVE', true)
->where(Query::filter()
->logic('or')
->where([
['ID', 1],
['LOGIN', 'admin']
])
)->exec();
// WHERE `main_user`.`ACTIVE` = 'Y' AND (`main_user`.`ID` = 1 OR `main_user`.`LOGIN` = 'admin')
Цепочка вызовов с OR
Вы также можете использовать цепочку вызовов для создания условий с OR:
use \Bitrix\Main\ORM\Query\Query;
\Bitrix\Main\UserTable::query()
->where('ACTIVE', true)
->where(Query::filter()
->logic('or')
->where('ID', 1)
->where('LOGIN', 'admin')
)
->exec();
// WHERE `main_user`.`ACTIVE` = 'Y' AND (`main_user`.`ID` = 1 OR `main_user`.`LOGIN` = 'admin')
Выражения
В фильтре можно использовать ExpressionField, который автоматически регистрируется как runtime поле. Это позволяет создавать сложные условия:
\Bitrix\Main\UserTable::query()
->where(new ExpressionField('LNG', 'LENGTH(%s)', 'LAST_NAME'), '>', 10)
->exec();
// WHERE LENGTH(`main_user`.`LAST_NAME`) > '10'
Для упрощения таких конструкций используйте хелпер. Хелпер — это вспомогательный метод, который упрощает работу с выражениями. Query::expr() является хелпером, который позволяет использовать SQL-функции:
use \Bitrix\Main\ORM\Query\Query;
\Bitrix\Main\UserTable::query()
->where(Query::expr()->length("LAST_NAME"), '>', 10)
->exec();
// WHERE LENGTH(`main_user`.`LAST_NAME`) > '10'
\Bitrix\Main\UserTable::query()
->addSelect(Query::expr()->count("ID"), 'CNT')
->exec();
// SELECT COUNT(`main_user`.`ID`) AS `CNT` FROM `b_user` `main_user`
Часто используемые SQL-выражения в хелпере:
-
count— подсчитывает количество строк -
countDistinct— подсчитывает количество уникальных значений -
sum— вычисляет сумму значений -
min— находит минимальное значение -
avg— вычисляет среднее значение -
max— находит максимальное значение -
length— определяет длину строки -
lower— преобразует строку в нижний регистр -
upper— преобразует строку в верхний регистр -
concat— объединяет несколько строк в одну
Совместимость с getList
При использовании getList, фильтр можно вставить вместо массива:
use \Bitrix\Main\ORM\Query\Query;
\Bitrix\Main\UserTable::getList([
'filter' => ['=ID' => 1]
]);
\Bitrix\Main\UserTable::getList([
'filter' => Query::filter()
->where('ID', 1)
]);
// WHERE `main_user`.`ID` = 1
Условия JOIN
Референсы — это связи между таблицами, которые позволяют объединять данные из разных таблиц. Они описываются с помощью ReferenceField:
new Entity\ReferenceField('GROUP', GroupTable::class,
Join::on('this.GROUP_ID', 'ref.ID')
)
Метод on — это сокращенная запись Query::filter() с предустановленным условием по колонкам. Он позволяет строить условия JOIN:
new Entity\ReferenceField('GROUP', GroupTable::class,
Join::on('this.GROUP_ID', 'ref.ID')
->where('ref.TYPE', 'admin')
->whereIn('ref.OPTION', [
new Column('this.OPTION1'),
new Column('this.OPTION2'),
new Column('this.OPTION3')
])
)
Везде, где указывается имя поля, можно указать любую цепочку переходов:
->whereColumn('this.AUTHOR.UserGroup:USER.GROUP.OWNER.ID', 'ref.ID');
Формат массива
Для использования фильтра в виде массива существует метод конвертации из массива в объект \Bitrix\Main\ORM\Query\Filter\ConditionTree::createFromArray. Формат массива:
$filter = [
['FIELD', '>', 2],
[
'logic' => 'or',
['FIELD', '<', 8],
['SOME', 9]
],
['FIELD', 'in', [5, 7, 11]],
['FIELD', '=', ['column' => 'FIELD2']],
['FIELD', 'in', [
['column' => 'FIELD1'],
['value' => 'FIELD2'],
['FIELD3']
]],
[
'negative' => true,
['FIELD', '>', 19]
],
];
Будьте осторожны при использовании массивов. Не подставляйте сырые данные, переданные пользователем, в качестве фильтра — они могут содержать опасные условия для раскрытия данных БД. Проверяйте все входящие условия через белый список полей.
Сравнение значений
-
Обычное сравнение:
['FIELD', '>', 2] -
С ключом
value:['FIELD', '>', ['value' => 2]] -
Сравнение с колонкой:
['FIELD1', '>', ['column' => 'FIELD2']]
Вложенные фильтры и логика
Вложенные фильтры передаются в виде вложенных массивов. Для отрицания negative() и изменения логики logic() используются одноименные ключи:
$filter = [
['FIELD', '>', 2],
[
'logic' => 'or',
['FIELD', '<', 8],
['SOME', 9]
],
[
'negative' => true,
['FIELD', '>', 19]
]
]
Операторы сравнения
Остальные методы where* заменяются соответствующими операторами сравнения in, between, like и так далее:
['FIELD', 'in', [5, 7, 11]]