Pagination logic is something that I've found myself redoing a number of times over the years, and each time it's been a relatively fiddly and painful process.
This time around I decided to check out the Zend_Paginator component from the Zend Framework, and found the process useful enough to share! In my case I was using Doctrine to retrieve data from the database. I'll skip most of the Doctrine-specific stuff, however, as hopefully this will end up as a decent example of how to integrate Paginator with other non-Zend libraries.
When the Paginator is instanced, it's given an instance of an appropriate Adapter and told what the current page is:
<?php
$paginator = new Zend_Paginator($adapter);
$paginator->setItemCountPerPage(20);
$paginator->setCurrentPageNumber(2);
It can then be used in a View using the Paginator View Helper:
<?php
echo $this->paginationControl(
$paginator,
'All',
'my_pagination_control.phtml'
);
The view helper is given an instance of the paginator, a pagination style, and a view script to use to render the pagination. Zend give a few example view scripts in their documentation. They're fairly simple in that they access a straightforward API to see which page would be next, previous, and which pages are in the current range.
So, what I needed to do was write an Adapter to work with Doctrine queries. I wanted it to take a query as a parameter and then paginate across the set of results. This turned out to be pretty simple - the Zend_Paginator_Adapter_Interface that I needed to implement only had 2 methods, count() and getItems(). The finished Adapter looks like this:
<?php
class My_Paginator_Adapter_DoctrineQuery
implements Zend_Paginator_Adapter_Interface
{
protected $_query;
protected $_count_query;
public function __construct($query)
{
$this->_query = $query;
$this->_count_query = clone $query;
}
public function getItems($offset, $itemsPerPage)
{
return $this->_query
->limit($itemsPerPage)
->offset($offset)
->execute();
}
public function count()
{
return $this->_count_query->count();
}
}
The count() method of course just returns the total results the query would return if run without constraints, while the getItems() method returns the items on the specified page.
Putting it all together, the code in my Action looks like this:
<?php
$query = Doctrine_Query::create()
->from('SomeTable')
->where('someField = ?', 12);
$adapter = new My_Paginator_Adapter_DoctrineQuery($query);
$paginator = new Zend_Paginator(
new My_Paginator_Adapter_DoctrineQuery($query));
$paginator->setItemCountPerPage(20);
$paginator->setCurrentPage($this->_request->getParam('page'));
$this->view->paginator = $paginator;
In the view I use the paginator to render out the pagination widget, but also for the view to obtain the objects it wants:
<?php
echo $this->paginationControl($this->paginator, 'All', 'my_pagination.phtml');
$items = $this->paginator->getCurrentPageItems();
foreach($items as $item) {
// ...
}
And a very simplified pagination partial would look like this:
<?php if (isset($this->previous)){ ?>
<a href="<?php echo $this->url(array('page' => $this->previous)); ?>">
prev
</a>
<?php } ?>
<?php foreach ($this->pagesInRange as $page){ ?>
<a href="<?php echo $this->url(array('page' => $page)); ?>">
<?php echo $page; ?>
</a>
<?php } ?>
<?php if (isset($this->next)){ ?>
<a href="<?php echo $this->url(array('page' => $this->next)); ?>">
next
</a>
<?php } ?>
Overall I like this way of working, and certainly it was easier to make a new Adapter than I'd feared. One future possibility is of making individual adaptors for specific queries, as a way of removing the query logic from my contoller.
1.
Awsome!. This may come very helpfull, now that Doctrine is a excelent tool to access database.
DavidZB
10th March 2010, 01:47