
Не так давно я начал работать с этим, довольно интересным фреймворком (буквально около месяца). Возник вопрос о реализации постраничного вывода новостей, и конечно, первый вариант — найти готовое решение. Перерыв кучу различных сайтов, я нашел достаточно большое количество готовых решений, но все они были выполнены в виде громоздких Бандлов (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 — навигация.