Encodera

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.

Оглавление

Постраничный вывод

Когда статей становится сотни, выводить их одним списком нельзя: страница грузится долго, а читать её неудобно. Постраничный вывод (пагинация) показывает по несколько записей и ссылки «назад» и «вперёд». В Laminas этим занимается пакет laminas-paginator.

Установка

composer require laminas/laminas-paginator laminas/laminas-paginator-adapter-laminasdb

Первый пакет — сам разбиватель на страницы, второй — адаптер, который умеет брать данные запросом laminas-db. Согласитесь вписать модуль Laminas\Paginator в config/modules.config.php.

Как работает Paginator

Объекту Paginator дают источник данных, размер страницы и номер текущей страницы. Простейший источник — массив:

use Laminas\Paginator\Adapter\ArrayAdapter;
use Laminas\Paginator\Paginator;
 
$paginator = new Paginator(new ArrayAdapter(range(1, 25)));
$paginator->setItemCountPerPage(10);
$paginator->setCurrentPageNumber(2);
 
foreach ($paginator as $number) {
    echo $number . ' ';
}

Цикл выведет числа от 11 до 20 — вторую страницу из трёх. Вызов count($paginator) возвращает число страниц.

Страницы из базы

Для таблицы используют адаптер DbSelect: он выполняет запрос с LIMIT и отдельно считает общее число строк. Добавьте метод в module/Blog/src/Model/PostTable.php:

// module/Blog/src/Model/PostTable.php
use Laminas\Paginator\Adapter\LaminasDb\DbSelect;
use Laminas\Paginator\Paginator;
 
public function paginate(int $page, int $perPage = 10): Paginator
{
    $select = $this->gateway->getSql()->select();
    $select->order('id DESC');
 
    $adapter = new DbSelect(
        $select,
        $this->gateway->getAdapter(),
        $this->gateway->getResultSetPrototype(),
    );
 
    $paginator = new Paginator($adapter);
    $paginator->setItemCountPerPage($perPage);
    $paginator->setCurrentPageNumber($page);
 
    return $paginator;
}

Третий аргумент DbSelect — образец результата: благодаря ему строки приходят объектами Post, как в остальных методах таблицы.

Контроллер

// module/Blog/src/Controller/PostController.php
public function indexAction()
{
    $page = max(1, (int) $this->params()->fromQuery('page', 1));
 
    return new ViewModel([
        'paginator' => $this->posts->paginate($page, 5),
    ]);
}

Номер страницы берётся из строки запроса: /blog?page=2.

Шаблон списка

<!-- module/Blog/view/blog/post/index.phtml -->
<ul>
    <?php foreach ($this->paginator as $post): ?>
        <li><?= $this->escapeHtml($post->title) ?></li>
    <?php endforeach; ?>
</ul>
 
<?= $this->paginationControl($this->paginator, 'Sliding', 'partial/paginator', ['route' => 'blog']) ?>

paginationControl() выводит ссылки с помощью отдельной части шаблона. Sliding — способ выбора номеров: показываются страницы вокруг текущей.

Шаблон ссылок

Создайте файл module/Blog/view/partial/paginator.phtml:

<!-- module/Blog/view/partial/paginator.phtml -->
<?php if ($this->pageCount > 1): ?>
    <nav>
        <?php if (isset($this->previous)): ?>
            <a href="<?= $this->url($this->route, [], ['query' => ['page' => $this->previous]]) ?>">Назад</a>
        <?php endif; ?>
 
        <?php foreach ($this->pagesInRange as $page): ?>
            <?php if ($page === $this->current): ?>
                <strong><?= $page ?></strong>
            <?php else: ?>
                <a href="<?= $this->url($this->route, [], ['query' => ['page' => $page]]) ?>"><?= $page ?></a>
            <?php endif; ?>
        <?php endforeach; ?>
 
        <?php if (isset($this->next)): ?>
            <a href="<?= $this->url($this->route, [], ['query' => ['page' => $this->next]]) ?>">Вперёд</a>
        <?php endif; ?>
    </nav>
<?php endif; ?>

Переменные pageCount, current, previous, next и pagesInRange передаёт сам помощник, а route — это четвёртый аргумент paginationControl(). Откройте /blog: под списком из пяти статей появятся номера страниц.

Частая ошибка

Загрузить все строки методом fetchAll() и разбить их ArrayAdapter — значит каждый раз читать из базы всю таблицу ради пяти записей. Для данных из базы используйте DbSelect: он запрашивает только строки текущей страницы.

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.