From 1499538df86283cde226dfa6fedc7298d5e1978d Mon Sep 17 00:00:00 2001 From: Andrei Nikolaev Date: Tue, 1 Sep 2026 17:29:21 +0300 Subject: [PATCH] Rewrite query builder article --- pages/orm/query-builder.md | 257 ++++++++++++++++++++++++++++++------- 1 file changed, 210 insertions(+), 47 deletions(-) diff --git a/pages/orm/query-builder.md b/pages/orm/query-builder.md index 0563f41..bf4eeb1 100644 --- a/pages/orm/query-builder.md +++ b/pages/orm/query-builder.md @@ -3,33 +3,62 @@ title: Построитель запросов description: 'Построитель запросов. ORM Bitrix Framework: ключевые концепции, примеры и рекомендации.' --- -Методы выборки `getList` и `getRow` сразу выполняют запросы и возвращают результаты. Они подходят для простых запросов, но неудобны, если параметры неизвестны заранее или нужна сложная логика. +Методы выборки `getList` и `getRow` сразу выполняют запросы и возвращают результаты, поэтому они хорошо подходят для простых запросов, но когда все параметры запроса заранее неизвестны или нужна сложная логика начинаются сложности. -**Гибкость с объектом Query.** Для гибкой настройки запросов используйте объект `Bitrix\Main\ORM\Query\Query`. Он накапливает параметры для запроса. Это полезно, когда параметры неизвестны заранее и формируются программно. +{% note info %} -Пример с `getList` +Все примеры ниже используют условную сущность `BookTable` с полями `ID`, `TITLE`, `ISBN`, `AUTHOR_ID`, `YEAR`, `PRICE`. -```php -// получение данных через getList -$result = BookTable::getList([ - 'select' => ['ISBN', 'TITLE', 'PUBLISH_DATE'], - 'filter' => ['=ID' => 1] -]); -``` +{% endnote %} -Пример с `Bitrix\Main\ORM\Query\Query` +Для гибкой настройки, построитель запросов использует объект `Bitrix\Main\ORM\Query\Query` - он накапливает параметры для запроса до его выполнения. -```php -use Bitrix\Main\ORM\Query\Query; +Посмотрите, как можно выразить один и тот же запрос к `BookTable` на получение конкретной книги с использованием разных подходов: -// аналогичный запрос через Query -$q = new Query(BookTable::getEntity()); -$q->setSelect(['ISBN', 'TITLE', 'PUBLISH_DATE']); -$q->setFilter(['=ID' => 1]); -$result = $q->exec(); -``` +{% list tabs %} + +- Пример с getList + + Получение данных через getList + + ```php + $result = BookTable::getList([ + 'select' => ['ISBN', 'TITLE', 'PUBLISH_DATE'], + 'filter' => ['=ID' => 1] + ]); + ``` + +- С использованием Query + + ```php + use Bitrix\Main\ORM\Query\Query; + + $q = new Query(BookTable::getEntity()); + $q->setSelect(['ISBN', 'TITLE', 'PUBLISH_DATE']); + $q->setFilter(['=ID' => 1]); + + $result = $q->exec(); + ``` + +- С использованием текучего синтаксиса + ```php + $q = BookTable::query() + ->setSelect(['ISBN', 'TITLE', 'PUBLISH_DATE']) + ->where('ID', 1) + ; + + $result = $q->exec(); + ``` + +{% endlist %} + +Объект `Query` — ключевой элемент для выборки данных. Именно он используется внутри `getList`/`getRow`. + +{% note warning %} + +В современном Bitrix Framework рекомендуется получать объект `Query` через статический метод `::query()` [соответствующей DataManager таблицы](*recomend_query), поскольку `Query` - это общий класс запроса и каждый DataManager-наследник вправе расширять его для своих технических нужд. -Объект `Query` — ключевой элемент для выборки данных и используется внутри `getList`. Однако переопределение методов `getList` может быть ограничено: метод может сработать при вызове, но не через `Query`. +{% endnote %} ## Постепенное добавление параметров @@ -38,7 +67,7 @@ $result = $q->exec(); ```php use Bitrix\Main\ORM\Query\Query; -$query = new Query(BookTable::getEntity()); +$query = BookTable::query(); attachSelect($query); attachOthers($query); $result = $query->exec(); @@ -71,7 +100,7 @@ function attachOthers(Query $query): void } ``` -**Создание объекта Query**. Используем `new Query(BookTable::getEntity())` для создания нового объекта `Query`, связанного с сущностью `BookTable`. Это будет основой для построения запроса. +**Создание объекта Query**. Используем `BookTable::query()` для создания нового объекта `Query`, связанного с сущностью `BookTable`. Это будет основой для построения запроса. **Добавление полей в запрос**. Функция `attachSelect` добавляет поля, которые нужно выбрать из базы данных. @@ -90,54 +119,188 @@ function attachOthers(Query $query): void Объект `Query` позволяет строить запрос без его выполнения. Это полезно для подзапросов или получения текста запроса: ```php -use Bitrix\Main\ORM\Query\Query; +use Bitrix\Main\Type\Date; + +$q = BookTable::query() + ->setSelect(['ID']) + ->setFilter([ + '=PUBLISH_DATE' => new Date('2014-12-13', 'Y-m-d') + ]) +; -$q = new Query(BookTable::getEntity()); -$q->setSelect(['ID']); -$q->setFilter(['=PUBLISH_DATE' => new Type\Date('2014-12-13', 'Y-m-d')]); $sql = $q->getQuery(); file_put_contents('/tmp/today_books.sql', $sql); -// Запрос "SELECT ID FROM my_book WHERE PUBLISH_DATE='2014-12-31'" будет сохранен в файл, но не выполнен. +// Запрос "SELECT ID FROM b_book WHERE PUBLISH_DATE='2014-12-13'" будет сохранен в файл, но не выполнен. ``` ## Методы Query -**select, group** +В данном разделе собраны примеры использования методов `Query`. + +### select, group + +- `setSelect`, `setGroup` — задаёт список полей, полностью заменяя предыдущие. +- `addSelect`, `addGroup` — добавляет новые поля к существующему списку. +- `getSelect`, `getGroup` — возвращает массив полей. -- `setSelect`, `setGroup` — задает список полей, полностью заменяя предыдущие +```php +$query = BookTable::query(); + +$query->setSelect(['ID', 'TITLE']); // список полей: ID, TITLE +$query->addSelect('PRICE'); // добавили PRICE к списку -- `addSelect`, `addGroup` — добавляет новые поля к существующему списку +print_r($query->getSelect()); +// ['ID', 'TITLE', 'PRICE'] + +$query->setSelect(['ID', 'ISBN']); // предыдущий список заменён +print_r($query->getSelect()); +// ['ID', 'ISBN'] +``` -- `getSelect`, `getGroup` — возвращает массив полей +```php +$query = BookTable::query() + ->setSelect(['AUTHOR_ID', 'YEAR']) +; -**distinct** +$query->setGroup('AUTHOR_ID'); // принимает строку или массив +$query->addGroup('YEAR'); // добавили YEAR к AUTHOR_ID -- `setDistinct` — устанавливает флаг `DISTINCT`, чтобы убрать дубликаты строк из результата +print_r($query->getGroup()); +// ['AUTHOR_ID', 'YEAR'] +``` -- `hasDistinct` — возвращает `true`, если флаг `DISTINCT` установлен или указан внутри выражения `ExpressionField`, добавленного в выборку +### distinct -**filter** +- `setDistinct` — устанавливает флаг `DISTINCT`, чтобы убрать дубликаты строк из результата. +- `hasDistinct` — возвращает `true`, если флаг `DISTINCT` установлен или указан внутри выражения `ExpressionField`, добавленного в выборку. -- `setFilter` — устанавливает фильтр +```php +// Получить уникальных авторов книг +$query = BookTable::query() + ->setSelect(['AUTHOR_ID']) + ->setDistinct() +; -- `addFilter` — добавляет параметр фильтра +$books = $query->fetchAll(); +// SQL: SELECT DISTINCT AUTHOR_ID FROM b_book -- `getFilter` — возвращает фильтр +if ($query->hasDistinct()) { + // ... +} +``` -**order** +```php +use Bitrix\Main\ORM\Fields\ExpressionField; -- `setOrder` — устанавливает порядок сортировки +// DISTINCT внутри выражения тоже делает выборку уникальной +$query = BookTable::query() + ->registerRuntimeField( + new ExpressionField('AUTHORS_CNT', 'COUNT(DISTINCT %s)', ['AUTHOR_ID']) + ) + ->setSelect(['AUTHORS_CNT']); -- `addOrder` — добавляет поле для сортировки +$query->hasDistinct(); // true, хотя setDistinct() не вызывали +``` -- `getOrder` — возвращает порядок сортировки +### filter -**limit/offset** +- `setFilter` — устанавливает фильтр. +- `addFilter` — добавляет параметр фильтра. +- `getFilter` — возвращает фильтр. -- `setLimit`, `setOffset` — устанавливает значение +```php +$query = BookTable::query(); -- `getLimit`, `getOffset` — возвращает значение +$query->setFilter(['>=PRICE' => 500]); // цена от 500 +$query->addFilter('AUTHOR_ID', 10); // добавили условие по автору -**runtime fields** +print_r($query->getFilter()); +// ['>=PRICE' => 500, 'AUTHOR_ID' => 10] +``` + +`setFilter` заменяет фильтр целиком, так же как и `setSelect` заменяет список полей. + + +{% note note %} + +Методы `setFilter` / `addFilter` работают со старым массивом фильтра. Для нового кода предпочтительны fluent-условия `where*()` и `Query::filter()`: + +```php +$books = BookTable::query() + ->setSelect(['ID', 'TITLE']) + ->where('AUTHOR_ID', 10) + ->where('PRICE', '>=', 500) + ->fetchAll() +; +``` + +{% endnote %} + + +### order + +- `setOrder` — устанавливает порядок сортировки. +- `addOrder` — добавляет поле для сортировки. +- `getOrder` — возвращает порядок сортировки. + +```php +$query = BookTable::query(); + +$query->setOrder(['TITLE' => 'ASC']); // сначала по названию +$query->addOrder('YEAR', 'DESC'); // затем свежие издания раньше + +print_r($query->getOrder()); +// ['TITLE' => 'ASC', 'YEAR' => 'DESC'] +``` + +### limit/offset + +- `setLimit`, `setOffset` — устанавливают значение. +- `getLimit`, `getOffset` — возвращают значение. + +```php +// Третья страница каталога: по 20 книг на страницу +$query = BookTable::query() + ->setSelect(['ID', 'TITLE']) + ->setLimit(20) + ->setOffset(40); // пропустить первые 40 записей + +$query->getLimit(); // 20 +$query->getOffset(); // 40 +``` + +### runtime fields + +- `registerRuntimeField` — регистрирует временное поле. + +Временное поле существует только внутри запроса: его вычисляет SQL, а в карту сущности оно не добавляется. В `registerRuntimeField` передавайте объект поля, например `ExpressionField`: + +```php +use Bitrix\Main\ORM\Fields\ExpressionField; + +$books = BookTable::query() + ->registerRuntimeField( + new ExpressionField('PRICE_WITH_VAT', '%s * 1.2', ['PRICE']) + ) + ->setSelect(['ID', 'TITLE', 'PRICE_WITH_VAT']) + ->fetchAll() +; +``` + +Runtime-поля можно использовать и в фильтре, и в сортировке: + +```php +use Bitrix\Main\ORM\Fields\ExpressionField; + +$books = BookTable::query() + ->registerRuntimeField( + new ExpressionField('PRICE_WITH_VAT', '%s * 1.2', ['PRICE']) + ) + ->setSelect(['ID', 'TITLE', 'PRICE_WITH_VAT']) + ->where('PRICE_WITH_VAT', '>', 1000) + ->setOrder(['PRICE_WITH_VAT' => 'DESC']) + ->fetchAll() +; +``` -- `registerRuntimeField` — регистрирует временное поле +[*recomend_query]: посмотрите на `BookTable::query()` на вкладке "С использованием текучего синтаксиса" \ No newline at end of file