Добавляем новостям пагинацию

Это последнее занятие нашего курса, в котором мы закрепим знания по работе с Composer и чужими классами, добавлением постраничной навигации разделу новостей.

Первым делом выбираем что-нибудь попроще на https://packagist.org/search/?q=pagination - мне приглянулся второй пункт, с kilte/pagination. Добавляем его в наш composer.json и устанавливаем на сервере.

Теперь пишем новый метод в Brevis\Controller:


    /**
     * Возвращает массив с постраничной навигацией
     *
     * @param $totalItems
     * @param int $currentPage
     * @param int $itemsPerPage
     * @param int $neighbours
     *
     * @return array
     */
    public function getPagination($totalItems, $currentPage = 1, $itemsPerPage = 10, $neighbours = 2) {
        $pagination = new Pagination($totalItems, $currentPage, $itemsPerPage, $neighbours);

        return $pagination->build();
    }

А в начале файла указываем

use \Kilte\Pagination\Pagination as Pagination;

Всё, в нашем проекте уже есть постраничная навигация.

Дальше мы научим контроллер новостей её использовать, а шаблон - оформлять.

Меняем Controllers\News

В прошлый раз мы написали контроллер новостей, и научили его выводит заметки по alias или id. Для приятной постраничной навигации, от вывода по id придётся отказаться, потому что адреса, типа /news/2/, будут у нас отдельными страницами.

То есть, /news/the-first - это заметка в разделе новостей, а /news/3/ - это третья страница новостей.

Добавляем новые свойства в класс, для разбивки на страницы:


    public $limit = 2;
    public $page = 1;
    private $_offset = 0;
    private $_total = 0;

limit у нас уже был, я просто уменьшил его для наглядности.

Теперь меняем метод initialize():


public function initialize(array $params = array()) {
        if (empty($params)) {
            $this->redirect("/{$this->name}/");
        }
        // После адреса страницы указан параметр
        elseif (!empty($params[0])) {
            // Указано число, значит это номер страницы
            // Реагируем только на вторую страницу и дальше
            if (is_numeric($params[0]) && $params[0] > 1) {
                // После номера нет косой, или наоборот, указано что-то еще
                if (!isset($params[1]) || !empty($params[1])) {
                    // Делаем редирект на канонический адрес
                    $this->redirect("/{$this->name}/$params[0]/");
                }
                // В противном случае, сохраняем номер страницы и считаем,
                // сколько строк нужно пропустить от начала в выборке
                $this->page = (int)$params[0];
                $this->_offset = ($this->page - 1) * $this->limit;
            }
            // Указано не число - это alias новости
            else {
                // Здесь всё осталось как раньше, только кода поменьше
                $c = $this->core->xpdo->newQuery('Brevis\Model\News', array('alias' => $params[0]));
                if ($news = $this->core->xpdo->getObject('Brevis\Model\News', $c)) {
                    $this->item = $news;
                }
            }
            // Если не выбрана заметка и offset пустой, то делаем редирект в корень раздела
            // Это будет в случае, если в параметрах указана какая-то ерунда
            if (!$this->_offset && !$this->item) {
                $this->redirect("/{$this->name}/");
            }
        }

        return true;
    }

В методе getItems() добавляем пропуск результатов от начала - offset, подсчёт общего количества строк и редирект в корень раздела, если указана несуществующая страница.


        $c = $this->core->xpdo->newQuery('Brevis\Model\News');
        // Считаем общее количество новостей
        $this->_total = $this->core->xpdo->getCount('Brevis\Model\News');
        // Если пропуск от начала больше, чем общее количество - указана несуществующая страница
        if ($this->_offset >= $this->_total) {
            // Редиректим в корень раздела
            $this->redirect("/{$this->name}/");
        }
        $c->select($this->core->xpdo->getSelectColumns('Brevis\Model\News', 'News'));
        $c->sortby('id', 'DESC');
        // А здесь, помимо лимита, добавляем и пропуск от начала
        $c->limit($this->limit, $this->_offset);

Остался последний штрих - нам нужно получить массив в пагинацией после вызова getItems(). Это мы делаем в методе run()


            $data = array(
                'title' => 'Новости',
                'pagetitle' => 'Новости',
                'items' => $this->getItems(),
                // Пагинация с нашими свойствами: total, page и limit
                'pagination' => $this->getPagination($this->_total, $this->page, $this->limit),
                'content' => '',
            );

Осталось оформить странички в шаблоне.

Меняем news.tpl

Как указано в документации kilte/pagination, компонент должен вернуть нам массив номеров страниц с указанием их типа: текущая, предыдущая и т.д.

Так что, просто добавляем прокрутку этого массива с оформлением в наш шаблон:


        {if $pagination}
            <nav>
                <ul class="pagination">
                    {foreach $pagination as $page => $type}
                        {switch $type}
                            {case 'first'}
                                <li><a href="/news/">«</a></li>
                            {case 'last'}
                                <li><a href="/news/{$page}/">»</a></li>
                            {case 'less', 'more'}
                            {case 'current'}
                                <li class="active"><a href="/news/{$page}/">{$page}</a></li>
                            {case default}
                                <li><a href="/news/{$page}/">{$page}</a></li>
                        {/switch}
                    {/foreach}
                </ul>
            </nav>
        {/if}

Думаю, тут всё понятно без комментариев. Обратите только внимание на то, что я пропускаю страницы more и less, потому что, на мой взгляд, они не нужны.

Вот и всё, наша постраничная навигация уже работает - можно проверять!

Заключение

Вот последний коммит с нашими изменениями.

Думаю, после прохождения курса, вам стало ясно, насколько легко и просто можно строить сайты самостоятельно, используя свои и чужие готовые решения, без CMS и тяжелых фреймворков.

Я ни в коем случае не призываю вас отказываться от них и делать всё самостоятельно, нет! Просто знайте, что где-то там внутри примерно такой же код, как мы написали с вами на этих курсах. Конечно, обычно он более продвинутый и крутой, но базовые принципы везде одинаковы.

Надеюсь, мои уроки помогут вам разбираться в PHP немного лучше. Если у вас остались вопросы - задавайте!

Оказалось, что я забыл написать еще один урок - вот и он.

← Предыдущая заметка
Выводим новости
Следующая заметка →
Учим контроллеры работать через Ajax
Комментарии (8)
Семён Лобачевский
26.06.2015 23:34

Спасибо за финальный урок и курс в целом! Стало более понятно как устроены php фреймворки и любимый MODx в частности)

bezumkinВасилий Наумкин
27.06.2015 01:10

На здоровье!

Похоже, ты единственный, кто всё прочитал =)

OnFoxПеретягин Илья
27.06.2015 02:31

Все впереди ))) Я первый урок осиливал около 3-4 дней, завтра буду читать второй, но если они по сложности идут по нарастающей, то до третьего не добраться ))))

bezumkinВасилий Наумкин
27.06.2015 10:02

Да ладно тебе! Делай как написано и через какое-то время придёт понимание. Тем более, всё закоммичено на GitHub, поэтапно.

Я так разработку компонентов для MODX осваивал - просто методом тыка повторял чужие разработки, пока не дошло, откуда какие файлы и зачем берутся.

OnFoxПеретягин Илья
27.06.2015 16:27

Тут больше проблема в самом коде, так как программирование больше хобби, то и опыта мало в этом, и некоторые моменты не понятны, приходиться вычитывать на других ресурсах, от сюда и время изучения.

На самом деле все толково написано, просто понадобиться больше времени чем ожидалось.

bezumkinВасилий Наумкин
27.06.2015 19:01

приходиться вычитывать на других ресурсах

Так спрашивай здесь, расскажу.

OnFoxПеретягин Илья
27.06.2015 23:01

Да там вопросы банальные, даже ка кто стыдно спрашивать..... Сейчас напишу один в первой теме

Алексей
22.08.2015 19:32

Ждём урока по Ajax )

born2slip
pishnaa istntome
22.11.2022 14:06
огромное спасибо! )
inetlover
Александр Наумов
14.11.2022 10:19
посмотри документацию. Спасибо, что-то она мне не нагуглилась. Это просто функции объединения для о...
bezumkin
Василий Наумкин
10.11.2022 05:46
Спасибо за поздравления!
inetlover
Александр Наумов
09.11.2022 17:08
Посмотрел в ДевТулсе свойство overscroll-behavior: none; присутствует, проверил в Chrome и Chromium ...
bezumkin
Василий Наумкин
03.11.2022 20:57
Поискать в исходниках ссылки на её адрес и поменять - скорее всего только nuxt.config.js. А зачем эт...
ni.kolokol@mail.ru
Николай Каленников
03.11.2022 19:43
Спасибо. Попробую тоже с нуля переставить
inetlover
Александр Наумов
03.11.2022 19:24
Спасибо!!! Все заработало!
bezumkin
Василий Наумкин
28.10.2022 05:23
В тексте есть подсказка // Контроллер требует новое разрешение protected $scope = &#x27;ord...
bezumkin
Василий Наумкин
27.10.2022 13:25
Понял, спасибо!
inetlover
Александр Наумов
23.10.2022 13:33
Понял, спасибо!