Pagination with the Null Adapter in ZF2

In this article, I will show you how to do pagination with the Zend\Paginator\Adapter\Null adapter. This adapter may seem pointless at first, but is very useful if you wish to have complete control on how the data is fetched while leveraging the features of the built-in pagination in regards to the representation.

Zend Framework 2 ships with a couple of adapters, where the DbSelect is typically the most interesting one. As the name indicates, this adapter can fetch the data to be displayed directly from the database – one page at a time of course. If you need to tweak its functionality to suit your needs, it is easy to extend the adapter.

However, there are many scenarios where it might make sense to control the fetching of the data by yourself. For instance, one might be using a service layer which accesses a data access layer (DAL), which in turn calls a stored procedure in a database. In such a scenario, the Zend\Paginator\Adapter\Null will come in handy.

Preparing the Paginator

Consider the example below where a list of articles are fetched independently of the paginator, but while still using the standard paginator for generating the pagination controls (i.e. page numbers or similar).


public function articlesAction() {
	$page = $this->params()->fromRoute('page');

	if (!empty($page)) {
		$page = (int) $page;
		$page = ($page < 1) ? 1 : $page; // If an invalid page is requested, then show the first page
	}

	// No page specified; show the first page
	else {
		$page = 1;
	}

	$itemsPerPage = 20;
	$results = $this->getArticleService()->getArticles($page, $itemsPerPage);

	$paginator = new \Zend\Paginator\Paginator(new \Zend\Paginator\Adapter\Null($results['count']));
	$paginator->setCurrentPageNumber($page);
	$paginator->setItemCountPerPage($itemsPerPage);
	
	return new ViewModel(array(
		'articles' => $results['articles'],
		'paginator' => $paginator,
		'route' => 'articles',
	));
}

The above controller action fetches and prepares a paginator for displaying a list of articles. We grab the page number from the matched route and make sure that the number is a positive integer. Then the data is fetched. In this example, it is fetched from a service layer, but this is of no importance in regards to the paginator. For the paginator to correctly display the total number of pages, we need to know how many matching records (usually database rows) exist. When using MySQL, this can be done by using SQL_CALC_FOUND_ROWS and FOUND_ROWS() as discussed in the MySQL manual.

We will use the total number of records (articles in this example) when instantiating the Zend\Paginator\Adapter\Null adapter. This adapter implements Zend\Paginator\Adapter\AdapterInterface, which extends the Countable interface. This means that the adapter must implement the count() and getItems() methods.

After we have instantiated the paginator and supplied it with the adapter, the current page number is set. This is for display purposes (which I will discuss in a moment), since we fetch the data ourselves. We also need to specify the number of items to display per page, as this will affect the total number of pages. We then assign three variables to the view model; the articles to display, the paginator, and the route that sent the request to the controller action. I will explain this in a moment.

Rendering the Pagination Controls

In the view, we will make use of the PaginationControl view helper, which helps us display the pagination controls.


echo $this->paginationControl($this->paginator, 'Elastic', array(
	'partial/pagination.phtml',
	'Shared',
), array('route' => $this->route));

For its first parameter, we pass the paginator that we created in the controller action. The second parameter is the scrolling style (more information in the official documentation). The third parameter is the partial to use when rendering the pagination controls (usually page numbers). This parameter can either be a string with the partial name or an array if your partial is located within a different module than the one in which you wish to render the pagination controls. In that case, the partial path (relative to the module’s view folder) and name is the first element in the array and the module name is the second.

For the above to work, it is important that you have added the path to your module’s view folder to the template path stack. For instance, in config/module.config.php, one could do like this:


return array(
	/* Some configuration omitted */

	'view_manager' => array(
		'template_path_stack' => array(
			'Shared' => __DIR__ . '/../view', // Note: the key is optional
		),
	),
);

The final parameter for the PaginationControl view helper is an array of parameters to pass to the partial. In the earlier example, we passed the route that we assigned to the view in the controller action. This is such that this route can be used when generating the pagination links within the partial – e.g. page number links.

To actually be able to render the pagination controls, you need to create the partial and put some code in it. The documentation has three different approaches for getting started, and those will be fine to get started with. You can then style them in any way you wish. By default, these partials use the supplied route and add a page number as a parameter to each link, rendering links such as /articles/page/4. These partials access several variables which are automatically passed to the partial by the PaginationControl view helper.

Conclusion

That is basically all there is to it. It may seem overly complicated at first, but once you have played around with it for a while, everything gradually begins to make sense. If you have any questions, please do not hesitate to ask in a comment.

4 Comments

  1. daniel said:

    Great, thanks for this example. Actually i need it with zend version 1 but is very inspiring.

    October 6, 2013
    Reply
  2. Vinay said:

    Where are your routes defined ?

    November 1, 2013
    Reply
    • Bo Andersen said:

      Hello Vinay,

      If you take a look at your module’s config/module.config.php file, then you should find a routes key, which is an array of routes. If one is not present in your module, then you can go ahead and create it yourself as per the official examples. I hope that helps.

      November 8, 2013
      Reply
  3. Dominik said:

    Wouldn’t it be better to inject the paginator with a controller factory? But i don’t know how to configure it the best way…

    April 2, 2014
    Reply

Leave a Reply

Your email address will not be published. Required fields are marked *