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

Это последнее занятие нашего курса, в котором мы закрепим знания по работе с 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 20:34

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

bezumkinВасилий Наумкин
26.06.2015 22:10

На здоровье!

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

OnFoxПеретягин Илья
26.06.2015 23:31

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

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

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

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

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

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

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

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

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

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

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

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

Алексей
22.08.2015 16:32

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

bezumkin
Василий Наумкин
09.04.2024 01:45
Ошибка 500 Это не похоже на ошибку Nginx, это скорее всего ошибка PHP - надо смотреть его логи. Во...
futuris
Futuris
04.04.2024 05:56
Я просто немного запутался. Когда в абзаце &quot;Vesp/Core&quot; ты пишешь про &quot;новый trait Fil...
bezumkin
Василий Наумкин
20.03.2024 18:21
Volledig!
Андрей
14.03.2024 10:47
Василий! Как всегда очень круто! Моё почтение!
russelgal
russel gal
09.03.2024 17:17
А этот стоило написать хотя бы затем, чтобы получить комментарий от юзера, который ничего не писал ...
inetlover
Александр Наумов
27.01.2024 00:06
Василий, спасибо! Извини, тупанул.
bezumkin
Василий Наумкин
22.01.2024 04:43
Давай-давай!
bezumkin
Василий Наумкин
24.12.2023 11:26
Спасибо!
bezumkin
Василий Наумкин
27.11.2023 02:43
Ура!
bezumkin
Василий Наумкин
25.11.2023 08:30
Vesp тянет 2 зависимости: vesp-frontent для фронта и vesp-core для бэкенда. Их можно обновлять, но э...