-
Notifications
You must be signed in to change notification settings - Fork 2.5k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #10444 from mpdude/paginator-dql-cacheable
Make Paginator-internal query cacheable in the query cache
- Loading branch information
Showing
8 changed files
with
206 additions
and
277 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Doctrine\ORM\Tools\Pagination; | ||
|
||
use Doctrine\ORM\Query\AST; | ||
use Doctrine\ORM\Query\SqlWalker; | ||
use Doctrine\ORM\Utility\PersisterHelper; | ||
use RuntimeException; | ||
|
||
use function count; | ||
use function reset; | ||
|
||
/** | ||
* Infers the DBAL type of the #Id (identifier) column of the given query's root entity, and | ||
* returns it in place of a real SQL statement. | ||
* | ||
* Obtaining this type is a necessary intermediate step for \Doctrine\ORM\Tools\Pagination\Paginator. | ||
* We can best do this from a tree walker because it gives us access to the AST. | ||
* | ||
* Returning the type instead of a "real" SQL statement is a slight hack. However, it has the | ||
* benefit that the DQL -> root entity id type resolution can be cached in the query cache. | ||
*/ | ||
final class RootTypeWalker extends SqlWalker | ||
{ | ||
public function walkSelectStatement(AST\SelectStatement $AST): string | ||
{ | ||
// Get the root entity and alias from the AST fromClause | ||
$from = $AST->fromClause->identificationVariableDeclarations; | ||
|
||
if (count($from) > 1) { | ||
throw new RuntimeException('Can only process queries that select only one FROM component'); | ||
} | ||
|
||
$fromRoot = reset($from); | ||
$rootAlias = $fromRoot->rangeVariableDeclaration->aliasIdentificationVariable; | ||
$rootClass = $this->getMetadataForDqlAlias($rootAlias); | ||
$identifierFieldName = $rootClass->getSingleIdentifierFieldName(); | ||
|
||
return PersisterHelper::getTypeOfField( | ||
$identifierFieldName, | ||
$rootClass, | ||
$this->getQuery() | ||
->getEntityManager() | ||
)[0]; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
55 changes: 55 additions & 0 deletions
55
tests/Doctrine/Tests/ORM/Tools/Pagination/RootTypeWalkerTest.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Doctrine\Tests\ORM\Tools\Pagination; | ||
|
||
use Doctrine\DBAL\Types\Type; | ||
use Doctrine\ORM\Query; | ||
use Doctrine\ORM\Tools\Pagination\RootTypeWalker; | ||
use Doctrine\Tests\DbalTypes\Rot13Type; | ||
use Doctrine\Tests\Models\ValueConversionType\AuxiliaryEntity; | ||
use Doctrine\Tests\Models\ValueConversionType\OwningManyToOneIdForeignKeyEntity; | ||
use Generator; | ||
|
||
class RootTypeWalkerTest extends PaginationTestCase | ||
{ | ||
protected function setUp(): void | ||
{ | ||
parent::setUp(); | ||
|
||
if (! Type::hasType('rot13')) { | ||
Type::addType('rot13', Rot13Type::class); | ||
} | ||
} | ||
|
||
/** | ||
* @dataProvider exampleQueries | ||
*/ | ||
public function testResolveTypeMapping(string $dqlQuery, string $expectedType): void | ||
{ | ||
$query = $this->entityManager->createQuery($dqlQuery); | ||
$query->setHint(Query::HINT_CUSTOM_OUTPUT_WALKER, RootTypeWalker::class); | ||
|
||
self::assertSame($expectedType, $query->getSQL()); | ||
} | ||
|
||
/** @return iterable<array{string, string}> */ | ||
public function exampleQueries(): Generator | ||
{ | ||
yield 'Entity with #Id column of special type' => [ | ||
'SELECT e.id4 FROM ' . AuxiliaryEntity::class . ' e', | ||
'rot13', | ||
]; | ||
|
||
yield 'Entity where #Id is a to-one relation with special type identifier' => [ | ||
'SELECT e FROM ' . OwningManyToOneIdForeignKeyEntity::class . ' e', | ||
'rot13', | ||
]; | ||
|
||
yield 'Simple integer ID in a query with a JOIN' => [ | ||
'SELECT u, g FROM Doctrine\Tests\ORM\Tools\Pagination\User u JOIN u.groups g', | ||
'integer', | ||
]; | ||
} | ||
} |
Oops, something went wrong.