Skip to content

Commit

Permalink
Added native query adapter
Browse files Browse the repository at this point in the history
  • Loading branch information
Nek- committed Jan 2, 2015
1 parent 0eec60b commit b14c64d
Show file tree
Hide file tree
Showing 2 changed files with 238 additions and 0 deletions.
121 changes: 121 additions & 0 deletions src/Pagerfanta/Adapter/DoctrineORMNativeQueryAdapter.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
<?php

/*
* This file is part of the Pagerfanta package.
*
* (c) Pablo Díez <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Pagerfanta\Adapter;

use Doctrine\ORM\NativeQuery;
use Doctrine\ORM\Query\ResultSetMapping;
use Pagerfanta\Exception\InvalidArgumentException;

/**
* @author Maxime Veber <[email protected]>
*/
class DoctrineORMNativeQueryAdapter implements AdapterInterface
{
/**
* @var NativeQuery
*/
private $nativeQuery;

/**
* @var callable
*/
private $countQueryBuilderModifier;

/**
* @var callable
*/
private $sliceQueryBuilderModifier;

/**
* @param NativeQuery $query A DBAL query builder.
* @param callable $countQueryBuilderModifier A callable to modify the query to count.
* @throws InvalidArgumentException
*/
public function __construct(NativeQuery $query, $countQueryBuilderModifier = null, $sliceQueryBuilderModifier = null)
{
if (strpos(strtolower($query->getSQL()), 'select') !== 0) {
throw new InvalidArgumentException('Only SELECT queries can be paginated.');
}

if ($countQueryBuilderModifier !== null && !is_callable($countQueryBuilderModifier)) {
throw new InvalidArgumentException('The count query builder modifier must be a callable.');
}
if ($sliceQueryBuilderModifier !== null && !is_callable($sliceQueryBuilderModifier)) {
throw new InvalidArgumentException('The slice query builder modifier must be a callable.');
}

$this->nativeQuery = $query;
$this->countQueryBuilderModifier = $countQueryBuilderModifier;
$this->sliceQueryBuilderModifier = $sliceQueryBuilderModifier;
}

/**
* {@inheritdoc}
*/
public function getNbResults()
{
$query = $this->cloneQuery();
$this->countQueryBuilder($query);

if ($this->countQueryBuilderModifier !== null) {
call_user_func($this->countQueryBuilderModifier, $query);
}

$res = $query->getScalarResult();

return $res[0]['res'];
}

/**
* {@inheritdoc}
*/
public function getSlice($offset, $length)
{
$query = $this->cloneQuery();

if ($this->countQueryBuilderModifier !== null) {
call_user_func($this->sliceQueryBuilderModifier, $query, $offset, $length);
} else {
$this->sliceQueryBuilder($query, $offset, $length);
}

return $query->getResult();
}

private function cloneQuery()
{
$query = clone $this->nativeQuery;
$query->setParameters($this->nativeQuery->getParameters());

return $query;
}

private function countQueryBuilder(NativeQuery $query)
{
$sql = explode(' FROM ', $query->getSql());
$sql[0] = 'SELECT COUNT(*) AS res';
$sql = implode(' FROM ', $sql);

$rsm = new ResultSetMapping();
$rsm->addScalarResult('res', 'res');

$query->setResultSetMapping($rsm);
$query->setSQL($sql);
}

private function sliceQueryBuilder(NativeQuery $query, $offset, $length)
{
$sql = $query->getSql();
$sql .= ' LIMIT ' . $offset . ', '. $length;
$query->setSQL($sql);
}
}
117 changes: 117 additions & 0 deletions tests/Pagerfanta/Tests/Adapter/DoctrineORMNativeQueryAdapterTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
<?php

namespace Pagerfanta\Tests\Adapter;

use Doctrine\ORM\NativeQuery;
use Doctrine\ORM\Query\ResultSetMappingBuilder;
use Doctrine\ORM\Tools\SchemaTool;
use Pagerfanta\Adapter\DoctrineORMNativeQueryAdapter;
use Pagerfanta\Tests\Adapter\DoctrineORM\DoctrineORMTestCase;
use Pagerfanta\Tests\Adapter\DoctrineORM\Group;
use Pagerfanta\Tests\Adapter\DoctrineORM\Person;
use Pagerfanta\Tests\Adapter\DoctrineORM\User;

class DoctrineORMNativeQueryAdapterTest extends DoctrineORMTestCase
{
private $user1;
private $user2;

public function setUp()
{
parent::setUp();

$schemaTool = new SchemaTool($this->entityManager);
$schemaTool->createSchema([
$this->entityManager->getClassMetadata('Pagerfanta\Tests\Adapter\DoctrineORM\User'),
$this->entityManager->getClassMetadata('Pagerfanta\Tests\Adapter\DoctrineORM\Group'),
$this->entityManager->getClassMetadata('Pagerfanta\Tests\Adapter\DoctrineORM\Person'),
]);

$this->user1 = $user = new User();
$this->user2 = $user2 = new User();
$group1 = new Group();
$group2 = new Group();
$group3 = new Group();
$user->groups[] = $group1;
$user->groups[] = $group2;
$user->groups[] = $group3;
$user2->groups[] = $group1;
$author1 = new Person();
$author1->name = 'Foo';
$author1->biography = 'Baz bar';
$author2 = new Person();
$author2->name = 'Bar';
$author2->biography = 'Bar baz';

$this->entityManager->persist($user);
$this->entityManager->persist($user2);
$this->entityManager->persist($group1);
$this->entityManager->persist($group2);
$this->entityManager->persist($group3);
$this->entityManager->persist($author1);
$this->entityManager->persist($author2);
$this->entityManager->flush();
}

public function testAdapterCount()
{
$sql = "SELECT * FROM User u";

$rsm = new ResultSetMappingBuilder($this->entityManager);
$rsm->addRootEntityFromClassMetadata('Pagerfanta\Tests\Adapter\DoctrineORM\User', 'u');

$nq = $this->entityManager->createNativeQuery($sql, $rsm);

$adapter = new DoctrineORMNativeQueryAdapter($nq);
$this->assertEquals(2, $adapter->getNbResults());
}

public function testAdapterCountFetchJoin()
{
$count = function (NativeQuery $query) {
$query->setSQL('SELECT COUNT(*) AS res FROM User u');
};

$rsm = new ResultSetMappingBuilder($this->entityManager);
$rsm->addRootEntityFromClassMetadata('Pagerfanta\Tests\Adapter\DoctrineORM\User', 'u');
$rsm->addJoinedEntityFromClassMetadata('Pagerfanta\Tests\Adapter\DoctrineORM\Group', 'g', 'u', 'groups', ['id' => 'user_id']);
$sql = "SELECT " . $rsm->generateSelectClause(['u' => 'u', 'g' => 'g']) .
" FROM User u INNER JOIN user_group ug ON u.id = ug.user_id LEFT JOIN groups g ON g.id = ug.group_id"
;

$nq = $this->entityManager->createNativeQuery($sql, $rsm);
$adapter = new DoctrineORMNativeQueryAdapter($nq, $count);
$this->assertEquals(2, $adapter->getNbResults());
}

public function testGetSlice()
{
$sql = "SELECT * FROM User u";

$rsm = new ResultSetMappingBuilder($this->entityManager);
$rsm->addRootEntityFromClassMetadata('Pagerfanta\Tests\Adapter\DoctrineORM\User', 'u');

$nq = $this->entityManager->createNativeQuery($sql, $rsm);

$adapter = new DoctrineORMNativeQueryAdapter($nq);
$this->assertEquals(1, count( $adapter->getSlice(0, 1)) );
$this->assertEquals(2, count( $adapter->getSlice(0, 10)) );
$this->assertEquals(1, count( $adapter->getSlice(1, 1)) );
}

public function testGetSliceFetchJoin()
{
$rsm = new ResultSetMappingBuilder($this->entityManager);
$rsm->addRootEntityFromClassMetadata('Pagerfanta\Tests\Adapter\DoctrineORM\User', 'u');
$rsm->addJoinedEntityFromClassMetadata('Pagerfanta\Tests\Adapter\DoctrineORM\Group', 'g', 'u', 'groups', ['id' => 'user_id']);
$sql = "SELECT " . $rsm->generateSelectClause(['u' => 'u', 'g' => 'g']) .
" FROM User u INNER JOIN user_group ug ON u.id = ug.user_id LEFT JOIN groups g ON g.id = ug.group_id"
;

$nq = $this->entityManager->createNativeQuery($sql, $rsm);
$adapter = new DoctrineORMNativeQueryAdapter($nq);
$this->assertEquals(1, count( $adapter->getSlice(0, 1)) );
$this->assertEquals(2, count( $adapter->getSlice(0, 10)) );
$this->assertEquals(1, count( $adapter->getSlice(1, 1)) );
}
}

0 comments on commit b14c64d

Please sign in to comment.