Класс ReversePageNavigation
Класс \Bitrix\Main\UI\ReversePageNavigation считает границы выборки от конца списка, а не от начала. Он наследует класс PageNavigation, переопределяет конструктор и четыре метода расчета. Остальные методы работают так же, как в базовом классе.
Обратную навигацию используйте для списков, где нумерация страниц идет от конца выборки: ленты записей, комментарии, сообщения. Страница с первым номером в таком списке всегда заканчивается последней записью выборки.
Чем обратная навигация отличается от прямой
Прямая навигация привязывает нумерацию к началу выборки. Первая страница всегда начинается с первой записи, а неполной оказывается последняя страница.
Обратная навигация привязывает нумерацию к концу выборки. Объект распределяет записи по страницам с конца, поэтому отдельной неполной страницы здесь не возникает.
|
Характеристика |
PageNavigation |
ReversePageNavigation |
|
Число записей |
Задают методом |
Передают в конструктор, второй аргумент обязателен |
|
Страница по умолчанию |
Первая |
С наибольшим номером |
|
Неполная страница |
Последняя по номеру |
Отдельной неполной страницы нет, остаток добавляется к странице с наибольшим номером |
|
Нулевое смещение |
На первой странице |
На странице с наибольшим номером |
Если отсчет страниц не привязан к концу списка, подойдет базовый класс PageNavigation: он не требует числа записей при создании и не дает отрицательных смещений.
Создать объект обратной навигации
Конструктор принимает два аргумента: идентификатор навигации и общее число записей.
use Bitrix\Main\UI\ReversePageNavigation;
use Bitrix\Main\UserTable;
$totalCount = UserTable::getCount();
$navigation = new ReversePageNavigation('users', $totalCount);
$navigation
->setPageSizes([10, 20, 50])
->setPageSize(20)
->initFromUri()
;
Второй аргумент обязателен, потому что без числа записей класс не может вычислить ни номер страницы по умолчанию, ни смещение: оба расчета ведутся от конца выборки. Изменить число записей позже можно методом setRecordCount().
Прием без подсчета записей, описанный в статье Постраничная навигация, к обратной навигации по той же причине неприменим. Приблизительное число записей здесь не подходит: от него зависит и номер страницы по умолчанию, и смещение каждой страницы. Для больших таблиц выбирайте прямую навигацию.
У обязательного аргумента есть вторая роль — он защищает от отрицательного смещения.
Номер страницы приходит из адреса, и он может оказаться больше, чем число страниц. Метод initFromUri() обрезает такой номер до максимального, но только если число записей уже задано. Для обратной навигации это критично: смещение считается от конца выборки, поэтому лишний номер уводит его в минус.
При 47 записях по 20 на странице получаются две страницы. Запрос третьей даст смещение -13, и выполнить его не удастся.
Размер страницы по этой же причине задавайте до initFromUri(). Вызов setPageSize() после него пересчитает число страниц, а номер текущей страницы останется прежним — и снова уйдет за границу.
Не задавайте номер страницы методом setCurrentPage() без собственной проверки. Метод не сверяет значение с числом страниц, поэтому номер больше getPageCount() даст отрицательное смещение.
Переопределенные методы
Класс переопределяет конструктор и четыре метода расчета. Типы результата совпадают с базовым классом, отличается только логика.
|
Метод |
PageNavigation |
ReversePageNavigation |
|
|
Принимает идентификатор навигации |
Принимает идентификатор навигации и общее число записей |
|
|
Делит число записей на размер страницы и округляет вверх. Неполная страница считается отдельной |
Делит число записей на размер страницы и отбрасывает остаток. Возвращает |
|
|
Возвращает |
Возвращает наибольший номер страницы, если номер не задан |
|
|
Отсчитывает смещение от начала выборки |
Отсчитывает смещение от конца выборки. На странице с наибольшим номером возвращает |
|
|
Возвращает размер страницы |
На странице с наибольшим номером возвращает размер страницы плюс остаток от деления, на остальных страницах — размер страницы |
Остальные методы — initFromUri(), addParams(), clearParams(), сеттеры и геттеры настроек — наследуются без изменений. Их контракты описаны в статье Класс PageNavigation.
Режим показа всех записей работает и здесь. Переопределенные методы учитывают его так же, как базовый класс: getPageCount() возвращает 1, getOffset() — 0, а getLimit() — общее число записей.
Как распределяются записи по страницам
При 47 записях и размере страницы 20 метод getPageCount() отбрасывает остаток и возвращает две страницы, а не три. Семь лишних записей не образуют отдельную страницу: объект добавляет их к странице с наибольшим номером.
Номера в объекте и в интерфейсе при этом не совпадают. Шаблоны компонента выводят номер по формуле «число страниц минус номер страницы плюс один», поэтому страницу с номером 2 компонент показывает посетителю как первую.
|
Номер в объекте и адресе |
Номер в интерфейсе |
|
|
Позиции записей в выборке |
|
2 — открывается по умолчанию |
1 |
0 |
27 |
записи 1–27 |
|
1 |
2 |
27 |
20 |
записи 28–47 |
У расчета две особенности.
-
Страница 2 начинается с первой записи выборки и содержит 27 записей — на семь больше заданного размера страницы.
-
Страница 1 заканчивается последней записью выборки.
Вторая особенность сохраняется при любом числе записей. Если записей станет 48, смещение страницы 1 вырастет до 28, и страница снова закончится последней записью выборки.
Из отброшенного остатка следует общее правило: объект не гарантирует ровно заданный размер на каждой странице. Число страниц растет только при переходе через значение, кратное размеру страницы, а весь остаток попадает на страницу с наибольшим номером. При размере 20 она содержит от 20 до 39 записей. Ровно 20 на каждой странице получится, только если число записей кратно размеру: при 40 записях остатка нет.
Когда записей меньше размера страницы, все они попадают на единственную страницу. Метод getLimit() при этом возвращает сумму размера и остатка: при семи записях и размере 20 это 27. Ограничение оказывается больше числа записей, но на результат запроса это не влияет — выборка вернет семь записей.
Пример. Выборка страницы обратной навигации.
use Bitrix\Main\UI\ReversePageNavigation;
use Bitrix\Main\UserTable;
$totalCount = UserTable::getCount();
$navigation = new ReversePageNavigation('users', $totalCount);
$navigation
->setPageSize(20)
->initFromUri()
;
$users = UserTable::query()
->setSelect(['ID', 'LOGIN'])
->setOrder(['ID' => 'ASC'])
->setOffset($navigation->getOffset())
->setLimit($navigation->getLimit())
->fetchCollection()
;
Порядок сортировки задает разработчик. Класс считает только смещение и ограничение, а какие записи окажутся в начале выборки, определяет метод setOrder().
Вывести обратную навигацию
Компонент bitrix:main.pagenavigation выводит обратную навигацию так же, как прямую. Отдельного параметра для этого нет: компонент проверяет тип переданного объекта и сам разворачивает порядок номеров страниц, а также расчет диапазона записей.
$APPLICATION->IncludeComponent('bitrix:main.pagenavigation', '', [
'NAV_OBJECT' => $navigation,
'SEF_MODE' => 'Y',
'SHOW_COUNT' => 'Y',
]);
Параметры компонента и его шаблоны описаны в статье Постраничная навигация.
Если выборка пуста, навигацию выводить нечего: при нулевом числе записей getPageCount() и getCurrentPage() возвращают 0. Базовый класс в такой ситуации вернул бы номер страницы 1, поэтому проверка вида getCurrentPage() == 1 в собственном шаблоне для обратной навигации не сработает.