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

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

Первым делом выбираем что-нибудь попроще на 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
Предыдущая заметка
Выводим новости


Комментарии ()

  1. Семён Лобачевский 26 июня 2015, 20:34 # +1
    Спасибо за финальный урок и курс в целом!
    Стало более понятно как устроены php фреймворки и любимый MODx в частности)
    1. Василий Наумкин 26 июня 2015, 22:10 # +1
      На здоровье!

      Похоже, ты единственный, кто всё прочитал =)
      1. Перетягин Илья 26 июня 2015, 23:31 # 0
        Все впереди ))) Я первый урок осиливал около 3-4 дней, завтра буду читать второй, но если они по сложности идут по нарастающей, то до третьего не добраться ))))
        1. Василий Наумкин 27 июня 2015, 07:02 # 0
          Да ладно тебе! Делай как написано и через какое-то время придёт понимание. Тем более, всё закоммичено на GitHub, поэтапно.

          Я так разработку компонентов для MODX осваивал — просто методом тыка повторял чужие разработки, пока не дошло, откуда какие файлы и зачем берутся.
          1. Перетягин Илья 27 июня 2015, 13:27 # 0
            Тут больше проблема в самом коде, так как программирование больше хобби, то и опыта мало в этом, и некоторые моменты не понятны, приходиться вычитывать на других ресурсах, от сюда и время изучения.

            На самом деле все толково написано, просто понадобиться больше времени чем ожидалось.
            1. Василий Наумкин 27 июня 2015, 16:01 # 0
              приходиться вычитывать на других ресурсах
              Так спрашивай здесь, расскажу.
              1. Перетягин Илья 27 июня 2015, 20:01 # 0
                Да там вопросы банальные, даже ка кто стыдно спрашивать…
                Сейчас напишу один в первой теме
    2. Алексей 22 августа 2015, 16:32 # 0
      Ждём урока по Ajax )
      Добавление новых комментариев отключено.