Pull to refresh

Symfony: pagination

image

Не так давно я начал работать с этим, довольно интересным фреймворком (буквально около месяца). Возник вопрос о реализации постраничного вывода новостей, и конечно, первый вариант — найти готовое решение. Перерыв кучу различных сайтов, я нашел достаточно большое количество готовых решений, но все они были выполнены в виде громоздких Бандлов (Bundles), или недоделанных, или не совсем сделанных хелперов (Helpers). Пришлось всё объединить, и вот что получилось.

И так, приступим. Для того чтобы выводить записи из базы постранично, нам нужно знать:
  • Их количество
  • Как их выбирать (тобишь сам запрос)

Т.к. мы работаем с конкретным фреймворком, то и разбирать будем и на более-менее конкретном примере.
Условимся, что у нас есть конкретный роутинг:

OurBundle_news_index:
pattern: /news/{page}
defaults: { _controller: OurBundle:controller_name:func_name, page: 1 }

OurBundle_pages:
pattern: /news/page/{page}
defaults: { _controller: OurBundle:controller_name:func_name, page: 1 }


Первая запись — алиас к стандартному виду новостей, дабы они были доступны по адресу /news и по /news/page/1. Параметр {page} не обязательный, если он не указан, передается как параметр 1.

Создадим хелпер:

namespace ...\OurBundle\Helpers;

class Paginator
{
private $_items;

private $_paginator;

private $_query;

private $_itemCount;

private $_url;

private $_maxItems;

private $_maxPages;

private $_currPage;

public function __construct($query, $itemCount, $url, $currPage = 1, $maxItems = 10, $maxPages = 20)
{
$this->_query = $query;
$this->_itemCount = $itemCount;
$this->_url = $url;
$this->_currPage = $currPage;
$this->_maxItems = $maxItems;
$this->_maxPages = $maxPages;

$this->paginate();
}

private function paginate()
{
$this->_query->setMaxResults($this->_maxItems)
->setFirstResult(($this->_currPage - 1) * $this->_maxItems);
$this->_items = $this->_query->getResult();

$this->_paginator = array();
$page_count = ceil($this->_itemCount / $this->_maxItems);
$lft = (($this->_currPage - (($this->_maxPages - 1) / 2)) > 0) ?
($this->_currPage - (($this->_maxPages - 1) / 2)) : 1;
$rgt = (($this->_currPage + (($this->_maxPages - 1) / 2)) < $page_count) ?
($this->_currPage + (($this->_maxPages - 1) / 2)) : $page_count;
if($lft > 1)
$this->_paginator[1] = 1;
if($lft > 2)
$this->_paginator['leftPoints'] = '...';
for($i = $lft; $i <= $rgt; $i++) {
$this->_paginator[$i] = $i;
}
if($rgt < $page_count - 1)
$this->_paginator['rightPoints'] = '...';
if($rgt < $page_count)
$this->_paginator[(int) $page_count] = (int) $page_count;

}

public function getItems()
{
return $this->_items;
}

public function __toString()
{
$result = '';
$result .= "Страницы: ";
foreach($this->_paginator as $page => $value )
{
if(is_integer($value) and $value != $this->_currPage)
{
if($value != 1)
$result .= "{$page}";
else
$result .= "1";
}
elseif($value != $this->_currPage)
$result .= "{$value}";
else
$result .= "{$value}";
}

return $result;
}

}


С этим классом думаю всё предельно ясно, переменные $maxItems и $maxPages отвечают за количество записей и за максимальное количество страниц соответственно.
Как же его использовать?

// считаем количество записей
$records_count = $em->createQuery(
"SELECT COUNT(p) FROM ..."
)
->getSingleResult();
$records_count = $records_count[1]; //весь массив нам не нужен, а нужно именно количество

// выбираем сами записи
$query = $em->createQuery(
"SELECT p FROM ..."
);
$paginator = new ...\OurBundle\Helpers\Paginator( //подключаем хелпер
$query, //запрос
$records_count, //количество записей
"/news/pages", //шаблон для генерации ссылок
$page //id странички, наш параметр {page}
);
$records = $paginator->getItems(); //выдёргиваем результат


Собственно вот и всё. В темплейт передаём $records и $paginator. В $records — записи на каждую страницу, $paginator — навигация.
Tags:
Hubs:
You can’t comment this publication because its author is not yet a full member of the community. You will be able to contact the author only after he or she has been invited by someone in the community. Until then, author’s username will be hidden by an alias.