Дата последнего изменения: 08.11.2023
Как реализовать постраничную навигацию в ядре D7 для выборок из ORM. (Доступно с версии 16.0.0)
Постраничная навигация - вспомогательный объект пользовательского интерфейса. В старом ядре постраничка была в объекте результата запроса, а чтобы выбрать с лимитами, приходилось вызывать специальную функцию CDBResult::NavQuery(), в которую передавался текст запроса. В D7 постраничная навигация - это просто вспомогательный объект пользовательского интерфейса. Первичным считается запрос, а постраничка просто помогает подставить правильные limit и offset.
Обязательность использования limit в запросах. Некорректно оставлять на долю php всю потенциально большую выборку без ограничений. Если выборка все-таки без лимитов, то возможно использование новой навигации, но разработчик это делает на свой страх и риск, без рекомендаций вендора. При этом необходимо решать вопросы использования разных БД и другие задачи.
Для постраничной навигации нужно знать количество записей. Как и в старом ядре для этого необходимо сделать отдельный запрос с COUNT
. На большом количестве записей это эффективнее полной выборки, а на маленьком - не имеет значения. Но если имеется не просто большая, а гигантская выборка, то постраничка на текущем ядре вполне справится и без COUNT
.
Постраничка поддерживает как параметры в GET, так и ЧПУ. Вместо глобальной переменной $NavNum
теперь нужно явно указывать идентификатор навигации. Поддерживаются URL следующего вида:
nav-cars
и nav-books
- идентификаторы двух разных постраничек на одной странице.
$nav = new \Bitrix\Main\UI\AdminPageNavigation("nav-culture"); $cultureList = CultureTable::getList(array( 'order' => array(strtoupper($by) => $order), 'count_total' => true, 'offset' => $nav->getOffset(), 'limit' => $nav->getLimit(), )); $nav->setRecordCount($cultureList->getCount()); $adminList->setNavigation($nav, Loc::getMessage("PAGES")); while($culture = $cultureList->fetch()) { }
'count_total' => true
- новый параметр, который заставляет ORM выполнить отдельный запрос COUNT
, результат которого оказывается в объекте результата выборки и может быть получен через $cultureList->getCount()
. Без этого параметра выполнять getCount() бесполезно и даже вредно (получите исключение).
$nav->getOffset()
возвращает позицию первой записи для текущей страницы навигации.
$nav->getLimit()
возвращает количество записей на странице или 0, если выбрано "все записи".
$nav->setRecordCount()
устанавливает количество записей для навигации.
Для удобства в административном отделе добавлена функция $adminList->setNavigation()
, которая просто подключает компонент main.pagenavigation с шаблоном admin.
<? $filter = array("=IBLOCK_ID"=>6); $nav = new \Bitrix\Main\UI\PageNavigation("nav-more-news"); $nav->allowAllRecords(true) ->setPageSize(5) ->initFromUri(); $newsList = \Bitrix\Iblock\ElementTable::getList( array( "filter" => $filter, "count_total" => true, "offset" => $nav->getOffset(), "limit" => $nav->getLimit(), ) ); $nav->setRecordCount($newsList->getCount()); while($news = $newsList->fetch()) { } ?> <? $APPLICATION->IncludeComponent( "bitrix:main.pagenavigation", "", array( "NAV_OBJECT" => $nav, "SEF_MODE" => "Y", ), false ); ?>
Добавилась инициализация навигации:
$nav->allowAllRecords(true) ->setPageSize(5) ->initFromUri();
В публичном разделе больше настроек типа "разрешить все записи", а это все нужно знать до того, как будет проинициализирован объект. (В административном разделе это уже переопределено классом-наследником.) Кроме того, необязательно получать текущую страницу из URL так как её можно взять где угодно и установить.
У компонента есть важный параметр "SEF_MODE" => "Y"
, по которому он генерирует ЧПУ постраничной навигации.
<? $filter = array("=IBLOCK_ID"=>6); $cnt = \Bitrix\Iblock\ElementTable::getCount($filter); $nav = new \Bitrix\Main\UI\ReversePageNavigation("nav-news", $cnt); $nav->allowAllRecords(true) ->setPageSize(5) ->initFromUri(); $newsList = \Bitrix\Iblock\ElementTable::getList( array( "filter" => $filter, "offset" => $nav->getOffset(), "limit" => $nav->getLimit(), ) ); while($news = $newsList->fetch()) { } ?> <? $APPLICATION->IncludeComponent( "bitrix:main.pagenavigation", "", array( "NAV_OBJECT" => $nav, "SEF_MODE" => "Y", ), false ); ?>
Здесь конструируется класс-наследник ReversePageNavigation и обязательно передаётся ему количество записей уже в конструкторе, т.к. обратная навигация математически не может работать без количества записей. Для этого в ORM обновился метод getCount(), теперь он принимает на вход параметр фильтра.
<? $filter = array("=IBLOCK_ID"=>6); $nav = new \Bitrix\Main\UI\PageNavigation("nav-grid-news"); $nav->allowAllRecords(true) ->setPageSize(5) ->initFromUri(); $newsList = \Bitrix\Iblock\ElementTable::getList( array( "filter" => $filter, "count_total" => true, "offset" => $nav->getOffset(), "limit" => $nav->getLimit(), ) ); $rows = array(); while($news = $newsList->fetch()) { $cols = array( "ID" => $news["ID"], "NAME" => $news["NAME"], ); $rows[] = array( "columns"=>$cols, ); } $nav->setRecordCount($newsList->getCount()); ?> <? $APPLICATION->IncludeComponent( "bitrix:main.interface.grid", "", array( "GRID_ID"=>"news_grid", "HEADERS"=>array( array("id"=>"ID", "name"=>"ID", "default"=>true), array("id"=>"NAME", "name"=>"Название", "default"=>true), ), "ROWS"=>$rows, "FOOTER"=>array(array("title"=>"Всего", "value"=>$newsList->getCount())), "NAV_OBJECT"=>$nav, "NAV_PARAMS"=>array( "SEF_MODE" => "Y" ), ) ); ?>
"NAV_PARAMS"=>array( "SEF_MODE" => "Y" )
- новый параметр, который по сути передаёт параметры в компонент навигации.
Возможна только прямая навигация. Принцип работы: разработчик выбирает чуть больше записей, чтобы определить, возможно ли показать "далее", а в своем цикле по записям разработчик определяет границу цикла.
<? $filter = array("=IBLOCK_ID"=>6); $nav = new \Bitrix\Main\UI\PageNavigation("nav-less-news"); $nav->allowAllRecords(true) ->setPageSize(5) ->initFromUri(); $newsList = \Bitrix\Iblock\ElementTable::getList( array( "filter" => $filter, "offset" => ($offset = $nav->getOffset()), "limit" => (($limit = $nav->getLimit()) > 0? $limit + 1 : 0), ) ); $n = 0; while($news = $newsList->fetch()) { $n++; if($limit > 0 && $n > $limit) { break; } } $nav->setRecordCount($offset + $n); ?> <? $APPLICATION->IncludeComponent( "bitrix:main.pagenavigation", "", array( "NAV_OBJECT" => $nav, "SEF_MODE" => "Y", "SHOW_COUNT" => "N", ), false ); ?>
$nav->setRecordCount($offset + $n);
Это делается для того, чтобы компонент навигации понял, что есть еще страницы. Но для шаблонов, которые выводят количество записей, мы передаем "SHOW_COUNT" => "N"
, чтобы они не пытались выводить неверную цифру (она будет верна только на последней странице).
COUNT
, то имеется в виду, что записей очень много. Поэтому не стоит разрешать все записи методом $nav->allowAllRecords(true)
.Файл с примерами.