Список заказов из кода - фильтры, постраничность, позиции
Строим список заказов для личного кабинета или отчёта так, чтобы он не грузил объект заказа на каждую строку и открывался на десятках тысяч записей.
Решение
Берём заказы табличной выборкой:
use Bitrix\Sale\Internals\OrderTable;
$res = OrderTable::getList([ 'select' => ['ID', 'ACCOUNT_NUMBER', 'DATE_INSERT', 'PRICE', 'STATUS_ID', 'PAYED'], 'filter' => ['=USER_ID' => $userId, '!=STATUS_ID' => 'CANCELED'], 'order' => ['ID' => 'DESC'], 'limit' => 20,]);Список строится из полей, а не из объектов. Загрузка объекта заказа поднимает корзину, оплаты, отгрузки и свойства, и двадцать таких загрузок на странице превращаются в сотни запросов к базе.
Делаем постраничный вывод:
$page = max(1, (int)($_GET['page'] ?? 1));$params = ['select' => ['ID', 'PRICE'], 'filter' => $filter, 'limit' => 20, 'offset' => ($page - 1) * 20, 'count_total' => true];$res = OrderTable::getList($params);printf("страница %d из %d\n", $page, (int)ceil($res->getCount() / 20));Общее число строк запрашивается тем же вызовом с отдельным признаком. Второй запрос ради подсчёта делает ту же работу дважды, а на большой таблице это заметно дороже самой выборки.
Забираем позиции всех заказов одним запросом:
use Bitrix\Sale\Internals\BasketTable;
$rows = BasketTable::getList([ 'select' => ['ORDER_ID', 'NAME', 'QUANTITY', 'PRICE'], 'filter' => ['@ORDER_ID' => array_column($orders, 'ID')],])->fetchAll();// раскладываем по заказам в памяти: запрос на каждый заказ здесь не нуженПозиции всех заказов страницы забирают одним запросом и раскладывают по заказам в памяти. Это то же правило, что и в каталоге: собранный список идентификаторов дешевле запроса в цикле.
Фильтруем по свойству заказа:
$res = OrderTable::getList([ 'select' => ['ID', 'ACCOUNT_NUMBER', 'PROP_VALUE' => 'PROPERTY.VALUE'], 'filter' => ['=PROPERTY.CODE' => 'CITY', '%=PROPERTY.VALUE' => 'Москва'], // связь со свойствами объявлена в самой сущности, объявлять её не нужно]);Свойства заказа лежат отдельной таблицей, и фильтр по ним означает соединение. Отбор по городу или телефону на большом магазине стоит дороже отбора по полям заказа, и на такие поля добавляют указатель в базе.
Объект заказа нужен только там, где идёт правка данных. Для чтения списка, отчёта и выгрузки достаточно полей, а загрузка объекта оправдана, когда заказ пересчитывают или сохраняют.
Отчёты по заказам стоит считать в базе, а не в памяти. Сумма за месяц и число заказов по статусам берутся группировкой в самом запросе, и разница на большом магазине измеряется секундами против минут.
Даты в фильтре передают объектом даты платформы, а не строкой. Строка в формате одного сайта на мультисайте с другим форматом даёт пустую выборку без всякой ошибки.
Типичные проблемы
Список заказов открывается несколько секунд.
На каждую строку списка загружается объект заказа целиком. Списку достаточно полей из обычной табличной выборки.
Постраничная навигация показывает неверное число страниц.
Общее число строк считается отдельным запросом с другим фильтром. Его берут тем же вызовом с признаком подсчёта строк.
Позиции подгружаются по одной на заказ.
Корзина запрашивается прямо внутри цикла по заказам. Позиции всех заказов страницы берут одним запросом по списку идентификаторов.
Фильтр по свойству заказа работает медленно.
Свойства лежат в отдельной таблице, и отбор означает соединение таблиц. Часто используемым полям добавляют указатель в базе.
Фильтр по дате ничего не находит.
Дата передана в фильтр строкой в формате сайта. В фильтр передают объект даты платформы, а не текст.
Частые вопросы
Чем табличная выборка отличается от загрузки заказа?
Она отдаёт поля и не собирает корзину, оплаты и отгрузки. Для чтения этого достаточно, для правки - нет.
Как показать покупателю только его заказы?
Фильтром по идентификатору пользователя. Проверять принадлежность заказа обязательно и на странице просмотра.
Можно ли выгрузить заказы в файл?
Да, той же выборкой порциями с записью в файл. Выгрузка всех заказов одним запросом упирается в память.
Где взять сумму оплаченного?
В полях заказа рядом с общей суммой. Считать её по оплатам вручную нужно только при сверке.
Смежное
- Заказы - оглавление подтемы
- Заказ из кода: чтение, поиск по номеру, изменение состава - работа с одним заказом
- Оплаты и отгрузки заказа: объекты, признаки, частичные операции - что внутри объекта заказа
- Личный кабинет: заказы покупателя, повтор и отмена - где список нужен чаще всего
- Каталог и продажи - устройство магазина целиком
- Отчёты по заказам: агрегаты, группировка, выгрузка - когда нужны не строки, а суммы