Дата последнего изменения: 27.10.2020
В обновлении main 17.5.2 в ORM появился новый фильтр.
Пример простейшего запроса:
\Bitrix\Main\UserTable::query() ->where("ID", 1) ->exec(); // WHERE `main_user`.`ID` = 1
Если нужен другой оператор сравнения, то он указывается явно:
\Bitrix\Main\UserTable::query() ->where("ID", "<", 10) ->exec(); // WHERE `main_user`.`ID` < 10
Пример с использованием IS NULL:
\Bitrix\Main\UserTable::query() ->whereNull("ID") ->exec(); // WHERE `main_user`.`ID` IS NULL
Для всех where* методов есть whereNot* аналоги. Пример:
\Bitrix\Main\UserTable::query() ->whereNotNull("ID") ->exec(); // WHERE `main_user`.`ID` IS NOT NULL
Помимо общего where, можно использовать следующие операторные методы:
whereNull($column) / whereNotNull($column) whereIn($column, $values|Query|SqlExpression) / whereNotIn($column, $values|Query|SqlExpression) whereBetween($column, $valueMin, $valueMax) / whereNotBetween($column, $valueMin, $valueMax) whereLike($column, $value) / whereNotLike($column, $value) whereExists($query|SqlExpression) / whereNotExists($query|SqlExpression)
Список операторов находится в \Bitrix\Main\Entity\Query\Filter\Operator::$operators
(см. ключи массива):
= , <> , != , < , <= , > , >= , in , between , like , exists
Отдельный метод whereColumn упрощает сравнение полей друг с другом:
\Bitrix\Main\UserTable::query() ->whereColumn('NAME', 'LOGIN') ->exec(); // WHERE `main_user`.`NAME` = `main_user`.`LOGIN`
Этот метод мало чем отличается от where, и формально это тот же самый вызов с небольшой оберткой:
\Bitrix\Main\UserTable::query() ->where('NAME', new Query\Filter\Expression\Column('LOGIN')) ->exec(); // WHERE `main_user`.`NAME` = `main_user`.`LOGIN`
whereColumn обеспечивает особую гибкость использования колонок в фильтре, например:
\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 выражение.
Для нескольких условий предполагается такая запись:
\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%'
Если необходимо указать несколько условий в одном вызове, то использовать такой формат: (операторные методы можно заменять кодами операторов)
\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%'
Для хранения всех условий фильтра в Query используется контейнер условий \Bitrix\Main\Entity\Query\Filter\ConditionTree
. Помимо стандартных условий, в него допускается добавление других экземпляров ConditionTree, таким образом создавая любой уровень ветвления и вложенности.
Все приведенные выше вызовы where - проксирование к базовому контейнеру. Следующие два вызова приведут к совершенно одинаковому результату:
\Bitrix\Main\UserTable::query() ->where([ ['ID', '>', 1], ['ACTIVE', true] ]) ->exec(); \Bitrix\Main\UserTable::query() ->where(Query::filter()->where([ ["ID", '>', 1], ['ACTIVE', true] ]))->exec(); // WHERE `main_user`.`ID` > 1 AND `main_user`.`ACTIVE` = 'Y'
Вместо массива использовался объект фильтра. Это позволяет создавать субфильтры и менять логику с AND на OR:
\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')
Допускается использование цепочки вызовов:
\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'
Для упрощения подобных конструкций добавлен хелпер, строящий вычисляемые поля:
\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 выражения:
Если вместо цепочки вызовов Query использовать getList, то фильтр вставляется в него вместо массива:
\Bitrix\Main\UserTable::getList([ 'filter' => ['=ID' => 1] ]); \Bitrix\Main\UserTable::getList([ 'filter' => Query::filter() ->where('ID', 1) ]); // WHERE `main_user`.`ID` = 1
Описания референсов представлено в таком формате:
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');