182  /  382
Справочник

Постраничная навигация

Просмотров: 117715
Дата последнего изменения: 08.11.2023
Татьяна Старкова
Сложность урока:
4 уровень - сложно, требуется сосредоточиться, внимание деталям и точному следованию инструкции.
1
2
3
4
5
Недоступно в лицензиях:
Ограничений нет

Как реализовать постраничную навигацию в ядре D7 для выборок из ORM. (Доступно с версии 16.0.0)

Принципы

Постраничная навигация - вспомогательный объект пользовательского интерфейса. В старом ядре постраничка была в объекте результата запроса, а чтобы выбрать с лимитами, приходилось вызывать специальную функцию CDBResult::NavQuery(), в которую передавался текст запроса. В D7 постраничная навигация - это просто вспомогательный объект пользовательского интерфейса. Первичным считается запрос, а постраничка просто помогает подставить правильные limit и offset.

Обязательность использования limit в запросах. Некорректно оставлять на долю php всю потенциально большую выборку без ограничений. Если выборка все-таки без лимитов, то возможно использование новой навигации, но разработчик это делает на свой страх и риск, без рекомендаций вендора. При этом необходимо решать вопросы использования разных БД и другие задачи.


Для постраничной навигации нужно знать количество записей. Как и в старом ядре для этого необходимо сделать отдельный запрос с COUNT. На большом количестве записей это эффективнее полной выборки, а на маленьком - не имеет значения. Но если имеется не просто большая, а гигантская выборка, то постраничка на текущем ядре вполне справится и без COUNT.

Инструменты для постраничной навигации

  • класс \Bitrix\Main\UI\PageNavigation для простой навигации;
  • класс \Bitrix\Main\UI\ReversePageNavigation для обратной навигации;
  • класс \Bitrix\Main\UI\AdminPageNavigation для навигации в админке;
  • компонент main.pagenavigation с шаблонами .default (основан на round старого компонента), admin (для админки), modern (для гридов).

Постраничка поддерживает как параметры в GET, так и ЧПУ. Вместо глобальной переменной $NavNum теперь нужно явно указывать идентификатор навигации. Поддерживаются URL следующего вида:

/page.php?nav-cars=page-5&nav-books=page-2&other=params
/page.php?nav-cars=page-5-size-20&nav-books=page-2
/page.php?nav-cars=page-all&nav-books=page-2
/dir/nav-cars/page-2/size-20/
/dir/nav-cars/page-all/?other=params
/dir/nav-cars/page-5/nav-books/page-2/size-10

где 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" ) - новый параметр, который по сути передаёт параметры в компонент навигации.

Постраничная навигация без COUNT

Возможна только прямая навигация. Принцип работы: разработчик выбирает чуть больше записей, чтобы определить, возможно ли показать "далее", а в своем цикле по записям разработчик определяет границу цикла.

<?
$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).

Файл с примерами.

39
Курсы разработаны в компании «1С-Битрикс»

Если вы нашли неточность в тексте, непонятное объяснение, пожалуйста, сообщите нам об этом в комментариях.
Развернуть комментарии