Перейти к содержимому

Постраничная навигация - страницы в выборке, свой вызов, кэш

Разбиваем выборку на страницы: размер страницы, вывод ссылок, два списка рядом и номер страницы в кэше.

Решение

Включаем разбиение в самой выборке:

$rs = CIBlockElement::GetList(['SORT' => 'ASC'], $filter,
false, ['nPageSize' => 20], ['ID', 'NAME', 'DETAIL_PAGE_URL']);
// номер страницы выборка сама берёт из адреса, из параметра PAGEN_1
// nPageSize делает два запроса: COUNT для навигации и сам SELECT
while ($row = $rs->GetNext()) { /* выводим элемент */ }

Размер страницы задают четвёртым параметром, а не ограничением выборки. Номер текущей страницы платформа читает из адреса сама, поэтому в коде его обычно трогать не приходится.

Платят за навигацию лишним запросом. Размер страницы заставляет платформу сначала посчитать общее число записей, поэтому для списков без навигации берут не его, а ограничение количества - одним запросом и без подсчёта.

Выводим ссылки на страницы:

$APPLICATION->IncludeComponent('bitrix:system.pagenavigation', 'modern', [
'NAV_RESULT' => $rs, // тот самый объект выборки
'SHOW_ALWAYS' => 'N', // при одной странице ссылки не нужны
], false);

Компонент печатает ссылки по данным выборки, поэтому вызывается после неё. Свой шаблон навигации кладут рядом с остальными шаблонами сайта и подключают по имени.

Считаем страницы в выборке на ORM:

$res = \Bitrix\Iblock\ElementTable::getList([
'select' => ['ID', 'NAME'], 'filter' => $filter,
'limit' => 20, 'offset' => ($page - 1) * 20,
'count_total' => true, // без него общее число неизвестно
]);
$pages = (int)ceil($res->getCount() / 20);

В новом ядре страницы считают руками: смещение задают явно, а общее количество запрашивают отдельным признаком. Тот же приём работает для заказов, своих таблиц и любых других выборок.

Разводим два списка на одной странице:

// первый вызов получает PAGEN_1, второй - PAGEN_2, третий - PAGEN_3
$news = CIBlockElement::GetList([], $f1, false, ['nPageSize' => 10], $select);
$docs = CIBlockElement::GetList([], $f2, false, ['nPageSize' => 10], $select);
// номера назначаются по порядку вызовов: менять порядок нельзя

Номер навигации привязан к порядку вызова, а не к списку. Перестановка блоков в шаблоне меняет номера, и сохранённые ссылки на вторую страницу новостей начинают листать документы.

Добавляем номер страницы в ключ кэша:

$page = (int)($_REQUEST['PAGEN_1'] ?? 1);
$cache->initCache(3600, 'list_' . $sectionId . '_' . $page, '/vendor/list');
// без номера страницы все страницы покажут содержимое первой

Кэш не знает про адрес, если номер страницы не попал в ключ. Ошибка выглядит обманчиво: первая страница работает, а остальные показывают тот же список, и списывают это на поисковых роботов.

Адреса вида /catalog/page/2/ собираются правилом обработки адресов. Правило разбирает номер и передаёт его выборке тем же параметром, а компонент навигации собирает ссылки обратно по шаблону из своих настроек.

Страницы навигации попадают в поисковый индекс как дубли. Что с ними делать - закрывать, склеивать каноническим адресом или оставлять - разобрано отдельно, в странице про дубли каталога.

Типичные проблемы

Навигация не выводится, показываются все элементы.

В параметрах выборки не задан размер страницы, и разбиение вообще не включилось. Размер задают четвёртым параметром выборки.

Вторая страница показывает содержимое первой.

Номер страницы не входит в ключ кэша, и все страницы отдаются из одной записи. В ключ добавляют текущий номер.

Ссылки второго списка листают первый.

Номера навигации назначаются по порядку вызовов на странице. После перестановки блоков в шаблоне номера меняются местами.

В адресе появляется лишний кусок с номером страницы.

Правило обработки адресов и шаблон ссылок в компоненте навигации собраны по-разному. Адрес разбирают и собирают по одному образцу.

На последней странице пусто, хотя элементы есть.

Фильтр выборки для подсчёта отличается от фильтра для вывода. Количество и список запрашивают одним и тем же условием.

Частые вопросы

Как сделать пагинацию для выборки заказов или своей таблицы?

Задать смещение и предел в параметрах запроса, а общее количество получить отдельным признаком подсчёта. Дальше число страниц считается делением, а ссылки печатает тот же компонент навигации.

Как задать навигацию компоненту, вызванному нештатно?

Передать объект выборки в компонент постраничной навигации параметром результата. Он сам разберёт текущую страницу и напечатает ссылки, ничего больше от вызывающего кода не требуется.

Как показать все элементы одной страницей?

Разрешить показ всех записей в параметрах разбиения либо не включать разбиение вовсе. На больших списках это опасно: страница со всеми элементами тянет память и время.

Почему при переходе на вторую страницу сбрасывается фильтр?

Ссылки навигации собираются без параметров фильтра, если их не передали в шаблон. Параметры отбора добавляют к адресу страницы вместе с номером.

Нужно ли закрывать страницы навигации от индексации?

Чаще их склеивают каноническим адресом, а не закрывают целиком: товары со второй страницы иначе выпадают из поиска. Подробный разбор - в статье про дубли страниц каталога.

Смежное

Первоисточник