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

18.12.2018

Теги: CMSБитриксБуферизацияКомпонентНавигацияПостраничнаяНавигацияСтароеЯдро

Постраничная навигация в Битрикс формируется и выводится с помощью системного компонента bitrix:system.pagenavigation. Компонент разработан для использования совместно с другими компонентами и тесно связан с ними. Посмотрим, как можно использовать этот компонент для постраничного вывода списка элементов инфоблока.

Системный компонент bitrix:system.pagenavigation

if (!CModule::IncludeModule('iblock')) {
    ShowError('Модуль «Информационные блоки» не установлен');
}

// какие поля элементов инфоблока выбираем
$arSelect = array(
    'ID',
    'CODE',
    'NAME',
    'PREVIEW_TEXT',
);
// условия выборки элементов инфоблока
$arFilter = array(
    'IBLOCK_ID' => 5,             // идентификатор инфоблока
    'IBLOCK_ACTIVE' => 'Y',       // инфоблок должен быть активным
    'SECTION_ID' => 16,           // идентификатор раздела инфоблока
    'INCLUDE_SUBSECTIONS' => 'Y', // включая элементы из подразделов
    'SECTION_ACTIVE' => 'Y',      // разделы элементов должны быть активными
    'ACTIVE' => 'Y',              // только активные элементы
    'ACTIVE_DATE' => 'Y',         // активные элементы с учетом даты
);
// сортировка элементов
$arSort = array(
    'SORT' => 'ASC'
);
// постраничная навигация
$arNavParams = array(
    'nPageSize' => 2,   // количество элементов на странице
    'bShowAll' => true, // показывать ссылку «Все элементы»?
);
// выполняем запрос к базе данных
$dbResult = CIBlockElement::GetList(
    $arSort,
    $arFilter,
    false,
    $arNavParams,
    $arSelect
);

ob_start(); // начинаем буферизацию вывода
$APPLICATION->IncludeComponent(
    'bitrix:system.pagenavigation',
    '',
    array(
        'NAV_TITLE'   => 'Элементы', // поясняющий текст для постраничной навигации
        'NAV_RESULT'  => $dbResult,  // результаты выборки из базы данных
        'SHOW_ALWAYS' => false       // показывать постраничную навигацию всегда?
    )
);
$navString = ob_get_clean(); // завершаем буферизацию вывода

while ($arElement = $dbResult->Fetch()) {
    echo '<h2>'.$arElement['NAME'].'</h2>', PHP_EOL;
    echo '<p>'.$arElement['PREVIEW_TEXT'].'</p>', PHP_EOL;
}

// выводим постраничную навигацию
echo $navString;

Шаблон .default откровенно кошмарный, так что есть смысл использовать modern или round:

$APPLICATION->IncludeComponent(
    'bitrix:system.pagenavigation',
    'modern',
    array(/*...*/)
);

$APPLICATION->IncludeComponent(
    'bitrix:system.pagenavigation',
    'round',
    array(/*...*/)
);

Метод CDBResult::GetPageNavString()

Можно упростить наш код, используя метод GetPageNavString класса CDBResult:

if (!CModule::IncludeModule('iblock')) {
    ShowError('Модуль «Информационные блоки» не установлен');
}

// какие поля элементов инфоблока выбираем
$arSelect = array(/*...*/);
// условия выборки элементов инфоблока
$arFilter = array(/*...*/);
// сортировка элементов
$arSort = array(/*...*/);
// постраничная навигация
$arNavParams = array(
    'nPageSize' => 2,   // количество элементов на странице
    'bShowAll' => true, // показывать ссылку «Все элементы»?
);
// выполняем запрос к базе данных
$dbResult = CIBlockElement::GetList(
    $arSort,
    $arFilter,
    false,
    $arNavParams,
    $arSelect
);
// html-код постраничной навигации
$navString = $dbResult->GetPageNavString(
    'Элементы', // поясняющий текст
    'modern',   // имя шаблона
    false       // показывать всегда?
);

while ($arElement = $dbResult->Fetch()) {
    echo '<h2>'.$arElement['NAME'].'</h2>', PHP_EOL;
    echo '<p>'.$arElement['PREVIEW_TEXT'].'</p>', PHP_EOL;
}

// выводим постраничную навигацию
echo $navString;

Как нетрудно догадаться, внутри метода GetPageNavString() идет подключение системного компонента bitrix:system.pagenavigation и весь вывод компонента записывается в буфер. В предыдущем примере мы делали это сами, а теперь просто вызываем метод GetPageNavString().

Метод CDBResult::NavStart()

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

if (!CModule::IncludeModule('iblock')) {
    ShowError('Модуль «Информационные блоки» не установлен');
}

// какие поля элементов инфоблока выбираем
$arSelect = array(/*...*/);
// условия выборки элементов инфоблока
$arFilter = array(/*...*/);
// сортировка элементов
$arSort = array(/*...*/);
// выполняем запрос к базе данных
$dbResult = CIBlockElement::GetList(
    $arSort,
    $arFilter,
    false,
    false, // никаких ограничений выборки для постраничной навигации
    $arSelect
);
while ($arElement = $dbResult->Fetch()) {
    echo '<h2>'.$arElement['NAME'].'</h2>', PHP_EOL;
    echo '<p>'.$arElement['PREVIEW_TEXT'].'</p>', PHP_EOL;
}

Теперь добавим постраничную навигацию с помощью CAllDBResult::NavStart():

if (!CModule::IncludeModule('iblock')) {
    ShowError('Модуль «Информационные блоки» не установлен');
}

// какие поля элементов инфоблока выбираем
$arSelect = array(/*...*/);
// условия выборки элементов инфоблока
$arFilter = array(/*...*/);
// сортировка элементов
$arSort = array(/*...*/);
// выполняем запрос к базе данных
$dbResult = CIBlockElement::GetList(
    $arSort,
    $arFilter,
    false,
    false, // никаких ограничений выборки для постраничной навигации
    $arSelect
);
/*
 * Кол-во элементов на странице 2, показывать ссылку «Все элементы»
 */
$dbResult->NavStart(2, true);
while ($arElement = $dbResult->Fetch()) {
    echo '<h2>'.$arElement['NAME'].'</h2>', PHP_EOL;
    echo '<p>'.$arElement['PREVIEW_TEXT'].'</p>', PHP_EOL;
}
/*
 * Показываем постраничную навигацию
 */
$dbResult->NavPrint('Элементы');

CDBResult::NavStart(
    int page_size = 10,
    bool show_all = true
)

Метод разбивает результат выборки на страницы. После вызова этого метода, методы

  • CDBResult::Fetch()
  • CDBResult::GetNext()
  • CDBResult::ExtractFields()
  • CDBResult::NavNext()

будут ограничены только текущей страницей (а не всей выборкой).

Постраничная навигация в компонентах

Добавить параметры постраничной навигации на страницу настройки компонента:

<?php
/*
 * Файл local/components/tokmakov/iblock.section/.parameters.php
 */
if (!defined('B_PROLOG_INCLUDED') || B_PROLOG_INCLUDED!==true) die();

/*
 * Настройки компонента
 */
$arComponentParameters = array(
    /*...*/
);

// настройка постраничной навигации
CIBlockParameters::AddPagerSettings(
    $arComponentParameters,
    'Элементы',  // $pager_title
    false,       // $bDescNumbering
    true,        // $bShowAllParam
);

// добавляем еще одну настройку — на случай, если раздел инфоблока не найден
CIBlockParameters::Add404Settings($arComponentParameters, $arCurrentValues);

Постраничная навигация в коде компонента:

<?php
/*
 * Файл local/components/infoblock/iblock.section/component.php
 */
if (!defined('B_PROLOG_INCLUDED') || B_PROLOG_INCLUDED!==true) die();

if (!CModule::IncludeModule('iblock')) {
    ShowError('Модуль «Информационные блоки» не установлен');
    return;
}

// запрещаем сохранение в сессии номера последней страницы 
// при стандартной постраничной навигации
CPageOption::SetOptionString('main', 'nav_page_in_session', 'N');

if (!isset($arParams['CACHE_TIME'])) {
    $arParams['CACHE_TIME'] = 3600;
}

// тип инфоблока
$arParams['IBLOCK_TYPE'] = trim($arParams['IBLOCK_TYPE']);
// идентификатор инфоблока
$arParams['IBLOCK_ID'] = intval($arParams['IBLOCK_ID']);

// количество элементов на страницу
$arParams['ELEMENT_COUNT'] = intval($arParams['ELEMENT_COUNT']);
if ($arParams['ELEMENT_COUNT'] <= 0) {
    $arParams['ELEMENT_COUNT'] = 3;
}

// учитывать права доступа при кешировании?
$arParams['CACHE_GROUPS'] = $arParams['CACHE_GROUPS']=='Y';

// показывать постраничную навигацию вверху списка элементов?
$arParams['DISPLAY_TOP_PAGER'] = $arParams['DISPLAY_TOP_PAGER']=='Y';
// показывать постраничную навигацию внизу списка элементов?
$arParams['DISPLAY_BOTTOM_PAGER'] = $arParams['DISPLAY_BOTTOM_PAGER']=='Y';
// поясняющий текст для постраничной навигации
$arParams['PAGER_TITLE'] = trim($arParams['PAGER_TITLE']);
// всегда показывать постраничную навигацию, даже если в этом нет необходимости
$arParams['PAGER_SHOW_ALWAYS'] = $arParams['PAGER_SHOW_ALWAYS']=='Y';
// имя шаблона постраничной навигации
$arParams['PAGER_TEMPLATE'] = trim($arParams['PAGER_TEMPLATE']);
// показывать ссылку «Все элементы», с помощью которой можно показать все элементы списка?
$arParams['PAGER_SHOW_ALL'] = $arParams['PAGER_SHOW_ALL']=='Y';

// получаем все параметры постраничной навигации, от которых будет зависеть кеш
$arNavParams = null;
$arNavigation = false;
if ($arParams['DISPLAY_TOP_PAGER'] || $arParams['DISPLAY_BOTTOM_PAGER']) {
    $arNavParams = array(
        'nPageSize' => $arParams['ELEMENT_COUNT'], // количество элементов на странице
        'bShowAll' => $arParams['PAGER_SHOW_ALL'], // показывать ссылку «Все элементы»?
    );
    $arNavigation = CDBResult::GetNavParams($arNavParams);
}

$cacheDependence = array($arParams['CACHE_GROUPS'] ? $USER->GetGroups() : false, $arNavigation);
if ($this->StartResultCache(false, $cacheDependence)) {

    /*
     * Получаем информацию о разделе инфоблока
     */

    // какие поля раздела инфоблока выбираем
    $arSelect = array(/*...*/);
    // условия выборки раздела инфоблока
    $arFilter = array(/*...*/);
    // выполняем запрос к базе данных
    $rsSection = CIBlockSection::GetList(array(), $arFilter, false, $arSelect);
    $arResult = $rsSection->GetNext();

    if ($arResult) {
        /*
         * Получаем элементы этого раздела инфоблока
         */

        // какие поля элементов выбираем
        $arSelect = array(/*...*/);
        // условия выборки элементов инфоблока
        $arFilter = array(/*...*/);
        // сортировка элементов
        $arSort = array(/*...*/);
        // выполняем запрос к базе данных
        $rsElements = CIBlockElement::GetList($arSort, $arFilter, false, $arNavParams, $arSelect);

        $arResult['ITEMS'] = array();
        while ($arItem = $rsElements->GetNext()) {
            $arResult['ITEMS'][] = $arItem;
        }

        /*
         * Постраничная навигация
         */
        $arResult['NAV_STRING'] = $rsElements->GetPageNavString(
            $arParams['PAGER_TITLE'],
            $arParams['PAGER_TEMPLATE'],
            $arParams['PAGER_SHOW_ALWAYS'],
            $this
        );

        $this->SetResultCacheKeys(
            /*...*/
        );
        $this->IncludeComponentTemplate();
    } else { // если раздел инфоблока не найден
        $this->AbortResultCache();
        \Bitrix\Iblock\Component\Tools::process404(/*...*/);
    }
}

// кэш не затронет все действия ниже, здесь работаем уже с другим $arResult
if (isset($arResult['ID'])) {
    /*...*/
}

Свой шаблон постраничной навигации

Чтобы кастомизировать постраничную навигацию, надо скопировать один из шаблонов системного компонента system.pagenavigation. Все содержимое папки modern копируем в папку pager:

  • bitrix/components/bitrix/system.pagenavigation/templates/modern
  • local/templates/.default/components/bitrix/system.pagenavigation/pager

Удаляем файл стилей style.min.css и создаем файл .description.php:

<?php
/*
 * Файл local/templates/.default/components/bitrix/system.navigation/pager/.description.php
 */
if(!defined("B_PROLOG_INCLUDED") || B_PROLOG_INCLUDED!==true) die();

$arTemplateDescription = array(
    "NAME" => GetMessage("SYS_PAGER_TMPL_NAME"),
    "DESCRIPTION" => GetMessage("SYS_PAGER_TMPL_DESCR"),
);
<?php
/*
 * Файл local/templates/.default/components/bitrix/system.navigation/pager/lang/ru/.description.php
 */
$MESS['SYS_PAGER_TMPL_NAME'] = 'Постраничная навигация';
$MESS['SYS_PAGER_TMPL_DESCR'] = 'Шаблон постраничной навигации';

И теперь нам надо отредактировать файл template.php. Распечатаем массив $arResult с помощью функции print_r():

Array
(
    [NavShowAlways] => 
    [NavTitle] => Статьи
    [NavRecordCount] => 11
    [NavPageCount] => 6
    [NavPageNomer] => 3
    [NavPageSize] => 2
    [bShowAll] => 
    [NavShowAll] => 
    [NavNum] => 1
    [bDescPageNumbering] => 
    [add_anchor] => 
    [nPageWindow] => 5
    [bSavePage] => 
    [sUrlPath] => /blog/
    [NavQueryString] => 
    [sUrlPathParams] => /blog/?
    [nStartPage] => 1
    [nEndPage] => 5
    [NavFirstRecordShow] => 5
    [NavLastRecordShow] => 6
)

В нашем случае показывается список из 11 статей, по две статьи на страницу. Всего получается 6 страниц, но на последней странице только одна статья.

  • NavShowAlways — всегда показывать постраничную навигацию
  • NavTitle — название списка элементов, например «Статьи» или «Новости»
  • NavRecordCount — общее количество статей (записей)
  • NavPageCount — общее количество страниц
  • NavPageNomer — номер текущей страницы
  • NavPageSize — количество статей на одну страницу
  • bShowAll — разрешено или нет показывать ссылку «Все статьи»
  • NavShowAll — равен true, если показываются все статьи, без постраничной навигации
  • NavNum — номер постраничной навигации (PAGEN_1, PAGEN_2, …)
  • bDescPageNumbering — использовать или нет обратную постраничную навигацию
  • nPageWindow — количество страниц, которые отображаются в постраничной навигации
  • bSavePage — равна true если в главном модуле отмечена опция «Запоминать последнюю открытую страницу»
  • sUrlPath — путь к странице относительно корня
  • NavQueryString — строка GET-параметров
  • nStartPage — номер первой страницы слева для текущей страницы
  • nEndPage — номер первой страница справа для текущей страницы
  • NavFirstRecordShow — порядковый номер первой статьи на текущей странице
  • NavLastRecordShow — порядковый номер последней статьи на текущей странице

По поводу переменной bSavePage:

// Пример запрещает сохранение в сессии номера последней страницы при стандартной
// постраничной навигации. По умолчанию в сессии запоминается последняя открытая
// страница постраничной навигации. 
CPageOption::SetOptionString("main", "nav_page_in_session", "N");

По поводу переменных nStartPage и nEndPage. Если текущая страница — третья (NavPageNomer=3), а показывается пять ссылок (nPageWindow=5), тогда nStartPage=1, а nEndPage=5:

<a href="...">1</a> <a href="...">2</a> <span>3</span> <a href="...">4</a> <a href="...">5</a>

Если текущая страница — четвертая (NavPageNomer=4), а показывается пять ссылок (nPageWindow=5), тогда nStartPage=2, а nEndPage=6:

<a href="...">2</a> <a href="...">3</a> <span>4</span> <a href="...">5</a> <a href="...">6</a>
По поводу переменных NavFirstRecordShow и NavLastRecordShow. У нас всего 11 статей на 6 страницах, по две статьи на страницу. Если текщая страница — третья, то NavFirstRecordShow=5, а NavLastRecordShow=6. На первой странице будут первая и вторая статьи, на второй странице — третья и четвертая, на третьей странице (текущей) — пятая и шестая.
<?php
/*
 * Файл local/templates/.default/components/bitrix/system.pagenavigation/pager/template.php
 */
if (!defined('B_PROLOG_INCLUDED') || B_PROLOG_INCLUDED!==true) die();

$this->setFrameMode(true);

// ссылка на первую страницу
$firstPageUrl = $arResult['sUrlPath'];
if (!empty($arResult['NavQueryString'])) {
    $firstPageUrl = $firstPageUrl.'?'.$arResult['NavQueryString'];
}
// ссылка на последнюю страницу
$lastPageUrl = $arResult['sUrlPath'];
if (!empty($arResult['NavQueryString'])) {
    $lastPageUrl = $lastPageUrl.'?'.$arResult['NavQueryString']
                   .'&amp;PAGEN_'.$arResult['NavNum'].'='.$arResult['NavPageCount'];
} else {
    $lastPageUrl = $lastPageUrl.'?PAGEN_'.$arResult['NavNum'].'='.$arResult['NavPageCount'];
}
?>

<ul class="pager">
    <?php if ($arResult['NavPageNomer'] > 1): /* ссылка на первую страницу */ ?>
        <li>
            <a href="<?= $firstPageUrl ?>" title="Первая">«</a>
        </li>
    <?php endif; ?>
    
    <?php for ($i = $arResult['nStartPage']; $i <= $arResult['nEndPage']; $i++): ?>
        <?php
        // ссылка на очередную страницу
        $pageUrl = $arResult['sUrlPath'];
        if (!empty($arResult['NavQueryString'])) {
            $pageUrl = $pageUrl.'?'.$arResult['NavQueryString'].'&amp;PAGEN_'.$arResult['NavNum'].'='.$i;
        } else {
            $pageUrl = $pageUrl.'?PAGEN_'.$arResult['NavNum'].'='.$i;
        }
        ?>
        <?php if ($arResult['NavPageNomer'] == $i): /* если это текущая страница */ ?>
            <li><span><?= $i; ?></span></li>
        <?php else: ?>
            <li><a href="<?= $pageUrl; ?>"><?= $i; ?></a></li>
        <?php endif; ?>
    <?php endfor; ?>

    <?php if ($arResult['NavPageNomer'] < $arResult['NavPageCount']): /* ссылка на последнюю страницу */ ?>
        <li>
            <a href="<?= $lastPageUrl; ?>" title="Последняя">»</a>
        </li>
    <?php endif; ?>
</ul>
/*
 * Файл local/templates/.default/components/bitrix/system.pagenavigation/pager/style.css
 */
.pager {
    overflow: hidden;
    margin: 10px 0;
}
    .pager li {
        list-style: none;
        float: left;
        margin-right: 15px;
    }
    .pager li:last-child {
        margin-right: 0;
    }
        .pager li a {
            display: block;
            width: 26px;
            height: 26px;
            text-align: center;
            background: #395985;
            font-size: 14px;
            line-height: 26px;
            color: #fff;
            text-decoration: none;
        }
        .pager li span {
            display: block;
            width: 26px;
            height: 26px;
            text-align: center;
            background: #e9751f;
            font-size: 14px;
            line-height: 26px;
            color: #fff;
        }

Дополнительно

Поиск: CMS • Битрикс • Буферизация • Компонент • Навигация • Постраничная навигация • Старое ядро • bitrix:system.pagenavigation • CDBResult • GetPageNavString • NavStart • NavPrint

Каталог оборудования
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Производители
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Функциональные группы
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.