Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to paginate a native query in Doctrine 2?

Doctrine 2 has the Doctrine\ORM\Tools\Pagination\Paginator class which can be used to paginate normal DQL queries.

However if I pass it a native query, I get this error:

Catchable fatal error: Argument 1 passed to Doctrine\ORM\Tools\Pagination\Paginator::cloneQuery() must be an instance of Doctrine\ORM\Query, instance of Doctrine\ORM\NativeQuery given

I've tried removing the type-hinting from the paginator class in the cloneQuery method, but this just gives further errors because other bits of the paginator class expect methods found in Query that aren't in NativeQuery.

Is there any easy way of paginating the native queries without needing to build a new paginator class or fetching every row from the database into an array?

like image 229
Gnuffo1 Avatar asked Oct 28 '25 14:10

Gnuffo1


2 Answers

I made my own paginator adapter class compatible with Zend_Paginator.

Probably won't be the most flexible since it relies on there being a " FROM " near the start of the query (see the count() method) but it's a relatively quick and easy fix.

/**
 * Paginate native doctrine 2 queries 
 */
class NativePaginator implements Zend_Paginator_Adapter_Interface
{
    /**
     * @var Doctrine\ORM\NativeQuery
     */
    protected $query;
    protected $count;

    /**
     * @param Doctrine\ORM\NativeQuery $query 
     */
    public function __construct($query)
    {
        $this->query = $query;
    }

    /**
     * Returns the total number of rows in the result set.
     *
     * @return integer
     */
    public function count()
    {
        if(!$this->count)
        {
            //change to a count query by changing the bit before the FROM
            $sql = explode(' FROM ', $this->query->getSql());
            $sql[0] = 'SELECT COUNT(*)';
            $sql = implode(' FROM ', $sql);

            $db = $this->query->getEntityManager()->getConnection();
            $this->count = (int) $db->fetchColumn($sql, $this->query->getParameters());
        }

        return $this->count;
    }

    /**
     * Returns an collection of items for a page.
     *
     * @param  integer $offset Page offset
     * @param  integer $itemCountPerPage Number of items per page
     * @return array
     */
    public function getItems($offset, $itemCountPerPage)
    {
        $cloneQuery = clone $this->query;
        $cloneQuery->setParameters($this->query->getParameters(), $this->query->getParameterTypes());

        foreach($this->query->getHints() as $name => $value)
        {
            $cloneQuery->setHint($name, $value);
        }

        //add on limit and offset
        $sql = $cloneQuery->getSQL();
        $sql .= " LIMIT $itemCountPerPage OFFSET $offset";
        $cloneQuery->setSQL($sql);

        return $cloneQuery->getResult();
    }
}
like image 125
Gnuffo1 Avatar answered Oct 31 '25 04:10

Gnuffo1


public function countTotalRecords($query, string $primaryKey = '*'): int
    {
        if ($query instanceof QueryBuilder) {
            $paginator = new Paginator($query->getQuery());
            return count($paginator);
        } else if ($query instanceof NativeQuery) {
            $rsm = new ResultSetMappingBuilder($query->getEntityManager());
            $rsm->addScalarResult('count', 'count');

            $sqlCount = "select count(".$primaryKey.") as count from (" . $query->getSQL() . ") as item";

            $count = $query->getEntityManager()->createNativeQuery($sqlCount, $rsm);

            if ($query->getParameter('limit')) {
                $query->setParameter('limit', null);
            }

            $count->setParameters($query->getParameters());

            return (int)$count->getSingleScalarResult();
        }
        return 0;
    }
like image 32
CaduElias Avatar answered Oct 31 '25 04:10

CaduElias



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!