Pagination is one of most common tasks we developers know. Almost every system/website I know has some kind of pagination.

Luckly, Zend Framework 2 comes with several paginator adapters out of the box allowing iteration through Arrays, Zend\Db\Sql\Select objects and Iterators. It also allows us to create custom adapters and that’s what we need to do in order to use Zend\Paginator with Doctrine 2.

In this post we are going to go over the required steps to create a Doctrine adapter for Zend\Paginator.

Step 1: Creating the adapter

What a surprise. Assuming the you are working on the Application Module create a file called Adapter.php on module/Application/src/Application/Paginator

module/Application/src/Application/Paginator/Adapter.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
<?php

namespace Application\Paginator;

use Zend\Paginator\Adapter\AdapterInterface;

class Adapter implements AdapterInterface
{
  protected $repository;

    /**
     * Construct
     *
     * @param \Doctrine\ORM\EntityRepository $repository Repository class
     */
    public function __construct($repository)
    {
      $this->repository = $repository;
    }

    /**
     * Returns an collection of items for a page.
     *
     * @param int $offset           Page offset
     * @param int $itemCountPerPage Number of items per page
     *
     * @return array
     */
    public function getItems($offset, $itemCountPerPage)
    {
      return $this->repository->getItems($offset, $itemCountPerPage);
    }

    /**
     * Count results
     *
     * @return int
     */
    public function count()
    {
      return $this->repository->count();
    }

}

Our adapter implements the Zend\Paginator\Adapter\AdapterInterface interface, so we need to implement the methods getItems() and count()

Notice the we are injecting the repository class in the adapter’s contructor. We are going to take care of this dependency in the next steps.

Step 2: Using Service Manager to create the Paginator Service

On ‘module/Application/Module.php’ we are going to use the getServiceConfig() to setup our Paginator Service and injecting the required dependencies. It looks like this:

module/Application/Module.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public function getServiceConfig()
{
  return array(
      'factories' => array(
          'Application\Paginator\User'        => function ($serviceManager) {

              $entityManager  = $serviceManager->get(
                  'Doctrine\ORM\EntityManager'
                  );

              $userRepository = $entityManager
              ->getRepository('Application\Entity\User');

              $adapter        = new PaginatorAdapter($userRepository);

              $page           = $serviceManager
              ->get('application')
              ->getMvcEvent()
              ->getRouteMatch()
              ->getParam('page');

              $paginator      = new Paginator($adapter);
              $paginator->setCurrentPageNumber($page)
                        ->setItemCountPerPage(5); // how many items per page

                        return $paginator;
                }
            )
  );
}

Step 3: Create your Entity and Repository class

In this tutorial we are using a User Entity that looks like this. It is located on ‘module/Application/src/Application/Entity’:

module/Application/src/Appllication/Entity/User.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
<?php
namespace Application\Entity;

use Doctrine\ORM\Mapping as ORM;

/**
 * @ORM\Entity(repositoryClass="Application\Entity\Repository\User")
 * @ORM\Table(name="user")
 */
class User
{
    /**
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     * @ORM\Column(type="integer")
     */
    protected $id;

    /**
     * @ORM\Column(type="string", length=200)
     */
    protected $name;

    /**
     * @ORM\Column(type="string", length=200)
     */
    protected $email;

    /**
     * @return mixed
     */
    public function getId()
    {
        return $this->id;
    }

    public function setId($id)
    {
        $this->id = $id;

        return $this;
    }

    public function setName($name)
    {
        $this->name = $name;

        return $this;
    }

    /**
     * @return mixed
     */
    public function getName()
    {
        return $this->name;
    }

    public function setEmail($email)
    {
        $this->email = $email;

        return $this;
    }

    /**
     * @return mixed
     */
    public function getEmail()
    {
        return $this->email;
    }
}

Observe this line: @ORM\Entity(repositoryClass="Application\Entity\Repository\User"). It will be in this class that we are going to implements the methods used by our custom adapter.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
<?php

namespace Application\Entity\Repository;

use Doctrine\ORM\EntityRepository;
use Doctrine\ORM\Query;

class User extends EntityRepository
{
    /**
     * Counts how many users there are in the database
     *
     * @return int
     */
    public function count()
    {
        $query = $this->getEntityManager()->createQueryBuilder();
        $query->select(array('u.id'))
            ->from('Application\Entity\User', 'u');

        $result = $query->getQuery()->getResult();

        return count($result);
    }

    /**
     * Returns a list of users
     *
     * @param int $offset           Offset
     * @param int $itemCountPerPage Max results
     *
     * @return array
     */
    public function getItems($offset, $itemCountPerPage)
    {
        $query = $this->getEntityManager()->createQueryBuilder();
        $query->select(array('u.id', 'u.name', 'u.email'))
            ->from('Application\Entity\User', 'u')
            ->setFirstResult($offset)
            ->setMaxResults($itemCountPerPage);

        $result = $query->getQuery()->getResult(Query::HYDRATE_ARRAY);

        return $result;
    }
}

We now have all set to start using the adapter.

Step 4: Using the paginator adapter on your controller

Using our adapter is pretty straight foward. All we need to do is call the service and pass it to the views,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?php

namespace Application\Controller;

use Zend\Mvc\Controller\AbstractActionController;
use Zend\View\Model\ViewModel;

class IndexController extends AbstractActionController
{
    public function indexAction()
    {
        $users = $this->getServiceLocator()->get('Application\Paginator\User');

        return new ViewModel(
            array(
                'users' => $users
            )
        );

    }
}

This is how our view should look like.

module/Application/view/application/index/index.phtml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
<div class="row">

    <div class="col-md-12">
        <div class="panel panel-default">
            <div class="panel-heading">
                <h3 class="panel-title">Users</h3>
            </div>
            <div class="panel-body">
                <div class=" table-responsive">

                    <table class="table table-striped table-hover table-condensed">
                        <thead>

                            <tr>
                                <th>#</th>
                                <th>Name</th>
                                <th>Email</th>
                            </tr>
                        </thead>
                        <tbody>
                            <?php foreach ($this->users as $user): ?>
                                <tr>
                                    <td><?php echo $user['id'] ?></td>
                                    <td><?php echo $user['name'] ?></td>
                                    <td><?php echo $user['email'] ?></td>
                                </tr>
                            <?php endforeach; ?>
                        </tbody>
                    </table>
                </div>

                <?php echo $this->paginationControl($this->users, 'Sliding', 'paginator.phtml'); ?>
            </div>
        </div>
    </div>

</div>

And the partial paginator file:

module/Application/view/application/partials/paginator.phtml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
<div class="row paginator">
    <div class="col-sm-6 col-xs-12 col-md-12 margin-bottom-20">
        Showing <?php echo $this->firstItemNumber ?>
        to <?php echo $this->lastItemNumber ?>
        of <?php echo $this->totalItemCount ?> entries
    </div>
    <?php if ($this->pageCount > 1): ?>
        <div class="col-sm-6 col-xs-12 col-md-12">
            <div class="pull-right">
                <ul class="pagination">
                    <!-- Previous page link -->
                    <?php if (isset($this->previous)): ?>
                        <li>
                            <a href="<?php echo $this->url('application/pagination', array('page' => $this->previous)); ?>">
                                <<
                            </a>
                        </li>
                    <?php else: ?>
                        <li class="disabled">
                            <a>
                                <<
                            </a>
                        </li>
                    <?php endif; ?>
                    <!-- Numbered page links -->
                    <?php foreach ($this->pagesInRange as $page): ?>
                        <?php if ($page != $this->current): ?>
                            <li>
                                <a href="<?php echo $this->url('application/pagination', array('page' => $page)); ?>">
                                    <?php echo $page; ?>
                                </a>
                            </li>
                        <?php else: ?>
                            <li class="active">
                                <a><?php echo $page; ?></a>
                            </li>
                        <?php endif; ?>
                    <?php endforeach; ?>

                    <!-- Next page link -->
                    <?php if (isset($this->next)): ?>
                        <li>
                            <a href="<?php echo $this->url('application/pagination', array('page' => $this->next)); ?>">
                                >>
                            </a>
                        </li>
                    <?php else: ?>
                        <li class="disabled">
                            <a>
                                >>
                            </a>
                        </li>
                    <?php endif; ?>
                </ul>
            </div>
        </div>
    <?php endif; ?>
</div>

This partial file, however, requires a little configuration. You need to tell your application to look for view files on partials folder:

module/Application/config/module.config.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
'view_manager'    => array(
    'display_not_found_reason' => true,
    'display_exceptions'       => true,
    'doctype'                  => 'HTML5',
    'not_found_template'       => 'error/404',
    'exception_template'       => 'error/index',
    'template_map'             => array(
        'layout/layout'           => __DIR__ . '/../view/layout/layout.phtml',
        'application/index/index' => __DIR__ . '/../view/application/index/index.phtml',
        'error/404'               => __DIR__ . '/../view/error/404.phtml',
        'error/index'             => __DIR__ . '/../view/error/index.phtml',
    ),
    'template_path_stack'      => array(
        __DIR__ . '/../view',
        __DIR__ . '/../view/application/partials', // Add this line
    ),
),

And finally, we need to add the route application/pagination that we are using on paginator.phtml

module/Application/config/module.config.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
'pagination' => array(
    'type'    => 'Segment',
        'options' => array(
                            'route'       => '[/:controller]/page[/:page]',
                            'constraints' => array(
                                'controller' => '[a-zA-Z][a-zA-Z0-9_-]*',
                                'page'       => '[0-9]+',
                            ),
                            'defaults'    => array(
                                'controller' => 'Application\Controller\Index',
                                'action'     => 'index',
                            ),
                        ),
                    ),

That’s it.

Checkout out this project’s source code on github:

https://github.com/rosantoz/zf2-paginator-doctrine

Comments