Постраничная навигация - страницы в выборке, свой вызов, кэш
Разбиваем выборку на страницы: размер страницы, вывод ссылок, два списка рядом и номер страницы в кэше.
Решение
Включаем разбиение в самой выборке:
$rs = CIBlockElement::GetList(['SORT' => 'ASC'], $filter, false, ['nPageSize' => 20], ['ID', 'NAME', 'DETAIL_PAGE_URL']);// номер страницы выборка сама берёт из адреса, из параметра PAGEN_1// nPageSize делает два запроса: COUNT для навигации и сам SELECTwhile ($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/ собираются правилом обработки адресов. Правило
разбирает номер и передаёт его выборке тем же параметром, а компонент навигации
собирает ссылки обратно по шаблону из своих настроек.
Страницы навигации попадают в поисковый индекс как дубли. Что с ними делать - закрывать, склеивать каноническим адресом или оставлять - разобрано отдельно, в странице про дубли каталога.
Типичные проблемы
Навигация не выводится, показываются все элементы.
В параметрах выборки не задан размер страницы, и разбиение вообще не включилось. Размер задают четвёртым параметром выборки.
Вторая страница показывает содержимое первой.
Номер страницы не входит в ключ кэша, и все страницы отдаются из одной записи. В ключ добавляют текущий номер.
Ссылки второго списка листают первый.
Номера навигации назначаются по порядку вызовов на странице. После перестановки блоков в шаблоне номера меняются местами.
В адресе появляется лишний кусок с номером страницы.
Правило обработки адресов и шаблон ссылок в компоненте навигации собраны по-разному. Адрес разбирают и собирают по одному образцу.
На последней странице пусто, хотя элементы есть.
Фильтр выборки для подсчёта отличается от фильтра для вывода. Количество и список запрашивают одним и тем же условием.
Частые вопросы
Как сделать пагинацию для выборки заказов или своей таблицы?
Задать смещение и предел в параметрах запроса, а общее количество получить отдельным признаком подсчёта. Дальше число страниц считается делением, а ссылки печатает тот же компонент навигации.
Как задать навигацию компоненту, вызванному нештатно?
Передать объект выборки в компонент постраничной навигации параметром результата. Он сам разберёт текущую страницу и напечатает ссылки, ничего больше от вызывающего кода не требуется.
Как показать все элементы одной страницей?
Разрешить показ всех записей в параметрах разбиения либо не включать разбиение вовсе. На больших списках это опасно: страница со всеми элементами тянет память и время.
Почему при переходе на вторую страницу сбрасывается фильтр?
Ссылки навигации собираются без параметров фильтра, если их не передали в шаблон. Параметры отбора добавляют к адресу страницы вместе с номером.
Нужно ли закрывать страницы навигации от индексации?
Чаще их склеивают каноническим адресом, а не закрывают целиком: товары со второй страницы иначе выпадают из поиска. Подробный разбор - в статье про дубли страниц каталога.
Смежное
- Подгрузка списка порциями: кнопка «Показать ещё» и прокрутка - та же навигация без перезагрузки страницы
- Выборки и кэш - оглавление подтемы
- Выборки из инфоблоков: GetList, ORM и разделы - откуда берётся сама выборка
- Кэширование своей выборки: ключ, теги, сброс - как устроен ключ кэша
- Дубли страниц каталога: канонический адрес, пагинация, фильтр - страницы навигации в поиске
- Витрина каталога: список раздела, сортировка, фильтр - готовый список с навигацией
- Инфоблоки - устройство инфоблоков целиком
- Постраничная навигация сбоит: разбор причин - если страницы повторяются или пусты