Pagination with Zend 2 and Doctrine 2

Classic functionality that nearly every application needs - pagination. In this article we'll be looking at a simple method of creating pagination when working with Doctrine 2 and Zend Framework 2 using a pagination view helper. 

This demonstration will reference pagination for a 'users' page. The module example is titled 'UserManagement' and has a controller 'users' with an action titled 'view' where we'll be listing all of users provided by a doctrine user repository. You can adjust the code accordingly to fit your requirements. 

To start with let's set up our routing so that we can pass through page numbers in the url. Update your routing in your modules module.config.php file to include a page parameter:

'user-management' => array(
                'type'    => 'Segment',
                'options' => array(
                    'route'    => '/users/[:action]/[page/:page]',
                    'constraints' => array(
                        'page' => '[0-9]*',
                    ),
                    'defaults' => array(
                        '__NAMESPACE__' => 'UserManagement\Controller',
                        'controller'    => 'Users',
                        'action'        => '[a-zA-Z][a-zA-Z0-9_-]*',
                    ),
                ),
            ),

This now allows us to create urls like the following:

http://yourapplication.dev/users/view/page/2

Next add a method to your user repository to retrieve some data using the doctrine pagination. The code below is a full repository for a 'user' entity:

<?php

namespace Application\Entity\Repository;

use Doctrine\ORM\EntityRepository;
use Doctrine\ORM\Tools\Pagination\Paginator;

/**
 * UserRepository
 *
 * This class was generated by the Doctrine ORM. Add your own custom
 * repository methods below.
 */
class UserRepository extends EntityRepository
{
    /**
     * Get paged users
     * @param int $offset
     * @param int $limit
     * @return Paginator
     */
    public function getPagedUsers($offset = 0, $limit = 10)
    {
        $em = $this->getEntityManager();
        $qb = $em->createQueryBuilder();

        $qb->select('u')
            ->from('\Application\Entity\User', 'u')
            ->orderBy('u.lastName')
            ->setMaxResults($limit)
            ->setFirstResult($offset);

        $query = $qb->getQuery();

        $paginator = new Paginator( $query );

        return $paginator;
    }
}

Next we need extract the page variable within the controller when it's provided in the url, and pass this to our repository to get the paged users - here's the view action within the users controller:

public function usersAction()
{
        $page = $this->params()->fromRoute('page', 1);
        # move to service
        $limit = 10;
        $offset = ($page == 0) ? 0 : ($page - 1) * $limit;
        $em = $this->getServiceLocator()->get('Doctrine\ORM\EntityManager');
        $pagedUsers = $em->getRepository('Application\Entity\User')->getPagedUsers($offset, $limit);
        # end move to service
        $viewModel = new ViewModel();
        $viewModel->setVariable( 'pagedUsers', $pagedUsers );
        $viewModel->setVariable( 'page', $page );

        return $viewModel;
}

Note for simplicity sake I've put all this code in the controller action, in practice I'd move the code between the comment markers into a service so that this logic could be called in one line like so:

$pagedUsers = $this->userService->getPagedUsers( $page );

For an example of how to set up a user service check out the code samples in an earlier article on unit testing.

Next we'll create our view that will display the results for the current page, here's the complete view with the reference to the paginationHelper that we'll create in the final step:

<?php $paging = $this->paginationHelper($pagedUsers, $page, '/users/view/', 10); ?>
<h1>Users</h1>
<table class="table table-striped">
    <thead>
<tr> <th>Name</th> <th>Username</th> </tr> </thead> <tbody> <?php foreach($pagedUsers as $user) { ?> <tr> <td><?php echo $this->escapeHtml($user->firstName) . ' ' . $this->escapeHtml($user->lastName); ?></td> <td><?php echo $this->escapeHtml($user->username); ?></td> </tr> <?php } ?> </tbody> </table> <div class="paging"> <?php echo $paging; ?> </div>


So in our repository using an offset and limit we know that the only results in $pagedUsers will be the users for the current page range. In the last step we now just need to create a pagination helper, it's up to you where you locate this, I'd recommend inside a directory like Application\View\Helper\:

<?php
namespace Application\View\Helper;

use Zend\View\Helper\AbstractHelper;

class PaginationHelper extends AbstractHelper
{
    private $resultsPerPage;
    private $totalResults;
    private $results;
    private $baseUrl;
    private $paging;
    private $page;

    public function __invoke($pagedResults, $page, $baseUrl, $resultsPerPage=10)
    {
        $this->resultsPerPage = $resultsPerPage;
        $this->totalResults = $pagedResults->count();
        $this->results = $pagedResults;
        $this->baseUrl = $baseUrl;
        $this->page = $page;

        return $this->generatePaging();
    }

    /**
     * Generate paging html
     */
    private function generatePaging()
    {
        # Get total page count
        $pages = ceil($this->totalResults / $this->resultsPerPage);

        # Don't show pagination if there's only one page
        if($pages == 1)
        {
            return;
        }

        # Show back to first page if not first page
        if($this->page != 1)
        {
            $this->paging = "<a href="" . $this->baseUrl . "page/1"><<</a>";
        }

        # Create a link for each page
        $pageCount = 1;
        while($pageCount <= $pages)
        {
            $this->paging .= "<a href="" . $this->baseUrl . "page/" . $pageCount . "">" . $pageCount . "</a>";
            $pageCount++;
        }

        # Show go to last page option if not the last page
        if($this->page != $pages)
        {
            $this->paging .= "<a href="" . $this->baseUrl . "page/" . $pages . "">>></a>";
        }

        return $this->paging;
    }
}

Now that we have our view helper, you just need to register it in your module.config.php file like so:

'view_helpers' => array(
        'invokables'=> array(
            'PaginationHelper' => 'Application\View\Helper\PaginationHelper'
) ),

And that's it! You can now generate a nice set of paginated results using Doctrine and Zend. One of the benefits of this technique is that you're only ever fetching the results you need and not the entire result set every time.

If you want to tidy up your links, here's some CSS to create the following design:

.paging a
{
    display:inline-block;
    padding:5px 10px;
    border:1px solid #cccccc;
    border-radius: 3px;
    margin-right:5px;
}

.paging a:hover
{
    background-color:#428bca;
    color:#ffffff;
    text-decoration: none;
}

.paging
{
    text-align:center;
}
Sign Up

NEXT: Getting started with Zend 2

This article provides you with an introduction to Zend 2, the changes from Zend 1 and a step-by-step tutorial on creating a hello world module.

comments powered by Disqus
Sign Up

Popular Tags

350x250

Need a web developer?

If you'd like to work with code synthesis on your next project get in touch via the contact page.