6

i want to use doctrine with zend_paginator

here some example query :

$allArticleObj = $this->_em->getRepository('Articles'); $qb = $this->_em->createQueryBuilder();

    $qb->add('select', 'a')
            ->add('from', 'Articles a')
            ->setFirstResult(0)
            ->setMaxResults(5);

is there any example code to show we can write a zend_paginator adapter for doctrine 2 query builder?

JasonMArcher
  • 14,195
  • 22
  • 56
  • 52
Sina Miandashti
  • 2,087
  • 1
  • 26
  • 40

4 Answers4

8

You don't need to implement Zend_Paginator_Adapter_Interface. It is already implement by Zend_Paginator_Adapter_Iterator.

You can simply pass Doctrines' Paginator to Zend_Paginator_Adapter_Iterator, which you pass to Zend_Paginator. Then you call Zend_Paginator::setItemCountPerPage($perPage) and Zend_Paginator::setCurrentPageNumber($current_page). Like this:

use Doctrine\ORM\Tools\Pagination as Paginator; // goes at top of file

SomeController::someAction() 
{
 $dql = "SELECT s, c FROM Square\Entity\StampItem s JOIN s.country c ".' ORDER BY '.  $orderBy . ' ' . $dir;
 
 $query = $this->getEntityManager()->createQuery($dql);
 $d2_paginator = new Paginator($query); \\
 
 $d2_paginator_iter = $d2_paginator->getIterator(); // returns \ArrayIterator object
 
 $adapter =  new \Zend_Paginator_Adapter_Iterator($d2_paginator_iter);
  
 $zend_paginator = new \Zend_Paginator($adapter);          
                       
 $zend_paginator->setItemCountPerPage($perPage)
            ->setCurrentPageNumber($current_page);

 $this->view->paginator = $zend_paginator; //Then in your view, use it just like your currently use     
}
 

Then you use paginator in the view script just like you ordinarly do.

Explanation:

Zend_Paginator's constructor can take a Zend_Paginator_Adapter_Interface, which Zend_Paginator_Adpater_Iterator implements. Now, Zend_Paginator_Adapter_Iterator's constructor takes an \Iterator interface. This \Iterator must also implement \Countable (as you can see by looking at Zend_Paginator_Adapter_Iterator's constructor). Since Paginator::getIterator() method returns an \ArrayIterator, it by definition it fits the bill (since \ArrayIterator implements both \Iterator and \Countable).

See this port from Doctrine 1 to Docrine 2 of the code for "Zend Framework: A Beginner's Guide" from Doctrine 1 to Doctrine: https://github.com/kkruecke/zf-beginners-doctrine2. It includes code for paginating with Zend_Paginator using Zend_Paginator_Adapter_Iterator with Doctrine 2' Doctrine\ORM\Tools\Pagination\Paginator.

Code is here (although it might not all work with latest DoctrineORM 2.2) but the example is valid: https://github.com/kkruecke/zf-beginners-doctrine2/tree/master/ch7

Community
  • 1
  • 1
Kurt Krueckeberg
  • 1,225
  • 10
  • 11
  • 6
    That implementation is not correct. Even though it sort of works, When you run $d2_paginator->getIterator() it retrieves ALL entities from the database, and then Zend paginator just filters it out. So if you have alot of records which need to be paginated, this approach will be very slow. – Pavel Dubinin May 29 '12 at 10:31
  • Agreed. This ends up being a 502 for me because the script takes too long. That's only for ~20K records too. Not scalable. – Ian Fosbery Mar 11 '14 at 14:39
2

I was very surprised how hard it was to find an adapter example that doesn't cause performance issues with large collections for Doctrine 2 and ZF1.

My soultion does use the the Doctrine\ORM\Tools\Pagination\Paginator from Doctrine 2.2 just like @Kurt's answer; however the difference that this will return the doctrine paginator (Which is it's self an \Iterator) from getItems without the entire query results being hydrated.

use Doctrine\ORM\Tools\Pagination\Paginator as DoctrinePaginator;
use Doctrine\ORM\Query;

class DoctrinePaginatorAdapter implements \Zend_Paginator_Adapter_Interface
{
    protected $query;

    public function __construct(Query $query)
    {
        $this->query = $query;
    }

    public function count()
    {
        return $this->createDoctrinePaginator($this->query)->count();
    }

    public function getItems($offset, $itemCountPerPage)
    {
        $this->query->setFirstResult($offset)
                    ->setMaxResults($itemCountPerPage);

        return $this->createDoctrinePaginator($this->query);
    }

    protected function createDoctrinePaginator(Query $query, $isFetchJoinQuery = true)
    {
        return new DoctrinePaginator($query, $isFetchJoinQuery);
    }

} 
AlexP
  • 9,906
  • 1
  • 24
  • 43
1

The upshot, of course, is that you have to implement the Zend_Paginator_Adapter_Interface, which essentially means implementing the two methods:

count()

getItems($offset, $perPage)

Your adapter would accept the Doctrine query as a constructor argument.

In principle, the getItems() part is actually straightforward. Simply, add the $offset and $perPage restrictions to the query - as you are doing in your sample - and execute the query.

In practice, it's the count() business that tends to be tricky. I'd follow the example of Zend_Paginator_Adapter_DbSelect, replacing the Zend_Db operations with their Doctrine analogues.

David Weinraub
  • 14,144
  • 4
  • 42
  • 64
1

Please change

use Doctrine\ORM\Tools\Pagination as Paginator;

to

use Doctrine\ORM\Tools\Pagination\Paginator as Paginator;
Aliaksei Kliuchnikau
  • 13,589
  • 4
  • 59
  • 72
tridem
  • 11
  • 1