Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 7df41ca

Browse files
committed
#7820 rewrote pagination parameter conversion to be isolated in WhereInWalker
As per discussion with @lcobucci, it is better to keep dragons where there be dragons, and this change does indeed rewrite the previous approach by moving the responsibility of type conversion on a query object from the `Paginator` to the `WhereInWalker`, which already has access to class metadata for the root of the selection (and can reliably detect the root of the selection too)
1 parent 39d2113 commit 7df41ca

7 files changed

Lines changed: 161 additions & 100 deletions

File tree

lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2017,7 +2017,7 @@ public function isIdentifierUuid()
20172017
*
20182018
* @param string $fieldName
20192019
*
2020-
* @return \Doctrine\DBAL\Types\Type|string|null
2020+
* @return string|null
20212021
*
20222022
* @todo 3.0 Remove this. PersisterHelper should fix it somehow
20232023
*/

lib/Doctrine/ORM/Query/ResultSetMapping.php

Lines changed: 0 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,6 @@
1919

2020
namespace Doctrine\ORM\Query;
2121

22-
use Doctrine\ORM\EntityManagerInterface;
23-
use Doctrine\ORM\Mapping\MappingException;
24-
use function array_keys;
25-
use function array_values;
26-
use function assert;
27-
2822
/**
2923
* A ResultSetMapping describes how a result set of an SQL query maps to a Doctrine result.
3024
*
@@ -589,40 +583,4 @@ public function addMetaResult($alias, $columnName, $fieldName, $isIdentifierColu
589583

590584
return $this;
591585
}
592-
593-
/**
594-
* Retrieves the DBAL type name for the single identifier column of the root of a selection.
595-
* Composite identifiers not supported!
596-
*
597-
* @internal only to be used by ORM internals: do not use in downstream projects! This API is a minimal abstraction
598-
* that only ORM internals need, and it tries to make sense of the very complex and squishy array-alike
599-
* structure inside this class. Some assumptions are coded in here, so here be dragons.
600-
*
601-
* @throws MappingException If the identifier is not a single field, or if metadata for its
602-
* owner is incorrect/missing.
603-
*/
604-
final public function getTypeOfSelectionRootSingleIdentifierColumn(EntityManagerInterface $em) : string
605-
{
606-
assert($this->isSelect);
607-
608-
if ($this->isIdentifierColumn !== []) {
609-
// Identifier columns are already discovered here: we can use the first one directly.
610-
assert($this->typeMappings !== []);
611-
612-
return $this->typeMappings[array_keys(array_values($this->isIdentifierColumn)[0])[0]];
613-
}
614-
615-
// We are selecting entities, and the first selected entity is our root of the selection.
616-
if ($this->aliasMap !== []) {
617-
$metadata = $em->getClassMetadata($this->aliasMap[array_keys($this->aliasMap)[0]]);
618-
619-
return $metadata->getTypeOfField($metadata->getSingleIdentifierFieldName());
620-
}
621-
622-
// We are selecting scalar fields - the first selected field will be assumed (!!! assumption !!!) as identifier
623-
assert($this->scalarMappings !== []);
624-
assert($this->typeMappings !== []);
625-
626-
return $this->typeMappings[array_keys($this->scalarMappings)[0]];
627-
}
628586
}

lib/Doctrine/ORM/Tools/Pagination/Paginator.php

Lines changed: 4 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -19,16 +19,12 @@
1919

2020
namespace Doctrine\ORM\Tools\Pagination;
2121

22-
use Doctrine\ORM\EntityManagerInterface;
23-
use Doctrine\ORM\Mapping\MappingException;
24-
use Doctrine\ORM\Query\Parser;
25-
use Doctrine\ORM\QueryBuilder;
22+
use Doctrine\ORM\NoResultException;
2623
use Doctrine\ORM\Query;
24+
use Doctrine\ORM\Query\Parser;
2725
use Doctrine\ORM\Query\ResultSetMapping;
28-
use Doctrine\ORM\NoResultException;
26+
use Doctrine\ORM\QueryBuilder;
2927
use function array_map;
30-
use function assert;
31-
use function current;
3228

3329
/**
3430
* The paginator can handle various complex scenarios with DQL.
@@ -163,12 +159,7 @@ public function getIterator()
163159
}
164160

165161
$whereInQuery = $this->cloneQuery($this->query);
166-
$em = $subQuery->getEntityManager();
167-
$connection = $em->getConnection();
168-
$idType = $this->getIdentifiersQueryScalarResultType($subQuery, $em);
169-
$ids = array_map(static function (array $row) use ($connection, $idType) {
170-
return $connection->convertToDatabaseValue(current($row), $idType);
171-
}, $foundIdRows);
162+
$ids = array_map('current', $foundIdRows);
172163

173164
$this->appendTreeWalker($whereInQuery, WhereInWalker::class);
174165
$whereInQuery->setHint(WhereInWalker::HINT_PAGINATOR_ID_COUNT, count($ids));
@@ -294,25 +285,4 @@ private function unbindUnusedQueryParams(Query $query): void
294285

295286
$query->setParameters($parameters);
296287
}
297-
298-
/**
299-
* Parses a query that is supposed to fetch a set of entity identifier only,
300-
* and retrieves the type of said identifier.
301-
*
302-
* @throws MappingException If metadata couldn't be loaded, or if there isn't a single
303-
* identifier for the given query.
304-
*/
305-
private function getIdentifiersQueryScalarResultType(
306-
Query $query,
307-
EntityManagerInterface $em
308-
) : ?string {
309-
$rsm = (new Parser($query))
310-
->parse()
311-
->getResultSetMapping();
312-
313-
assert($rsm !== null);
314-
assert($rsm->isSelect);
315-
316-
return $rsm->getTypeOfSelectionRootSingleIdentifierColumn($em);
317-
}
318288
}

lib/Doctrine/ORM/Tools/Pagination/WhereInWalker.php

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
namespace Doctrine\ORM\Tools\Pagination;
2121

22+
use Doctrine\ORM\Mapping\ClassMetadataInfo;
2223
use Doctrine\ORM\Query\AST\ArithmeticExpression;
2324
use Doctrine\ORM\Query\AST\SimpleArithmeticExpression;
2425
use Doctrine\ORM\Query\TreeWalkerAdapter;
@@ -32,6 +33,9 @@
3233
use Doctrine\ORM\Query\AST\ConditionalExpression;
3334
use Doctrine\ORM\Query\AST\ConditionalFactor;
3435
use Doctrine\ORM\Query\AST\WhereClause;
36+
use function array_map;
37+
use function assert;
38+
use function is_array;
3539

3640
/**
3741
* Replaces the whereClause of the AST with a WHERE id IN (:foo_1, :foo_2) equivalent.
@@ -83,6 +87,7 @@ public function walkSelectStatement(SelectStatement $AST)
8387

8488
$fromRoot = reset($from);
8589
$rootAlias = $fromRoot->rangeVariableDeclaration->aliasIdentificationVariable;
90+
/** @var \Doctrine\ORM\Mapping\ClassMetadataInfo $rootClass */
8691
$rootClass = $queryComponents[$rootAlias]['metadata'];
8792
$identifierFieldName = $rootClass->getSingleIdentifierFieldName();
8893

@@ -104,6 +109,7 @@ public function walkSelectStatement(SelectStatement $AST)
104109
$expression = new InExpression($arithmeticExpression);
105110
$expression->literals[] = new InputParameter(":" . self::PAGINATOR_ID_ALIAS);
106111

112+
$this->convertWhereInIdentifiersToDatabaseValue($this->getTypeOfSingleIdentifierColumn($rootClass));
107113
} else {
108114
$expression = new NullComparisonExpression($pathExpression);
109115
$expression->not = false;
@@ -147,4 +153,39 @@ public function walkSelectStatement(SelectStatement $AST)
147153
);
148154
}
149155
}
156+
157+
private function convertWhereInIdentifiersToDatabaseValue(string $type) : void
158+
{
159+
$query = $this->_getQuery();
160+
$identifiersParameter = $query->getParameter(self::PAGINATOR_ID_ALIAS);
161+
162+
assert($identifiersParameter !== null);
163+
164+
$identifiers = $identifiersParameter->getValue();
165+
166+
assert(is_array($identifiers));
167+
168+
$connection = $this->_getQuery()
169+
->getEntityManager()
170+
->getConnection();
171+
172+
$query->setParameter(self::PAGINATOR_ID_ALIAS, array_map(static function ($id) use ($connection, $type) {
173+
return $connection->convertToDatabaseValue($id, $type);
174+
}, $identifiers));
175+
}
176+
177+
private function getTypeOfSingleIdentifierColumn(ClassMetadataInfo $class) : string
178+
{
179+
$identifierField = $class->getSingleIdentifierFieldName();
180+
181+
if ($class->hasField($identifierField)) {
182+
return (string) $class->getTypeOfField($identifierField);
183+
}
184+
185+
return $this->getTypeOfSingleIdentifierColumn(
186+
$this->_getQuery()
187+
->getEntityManager()
188+
->getClassMetadata($class->getAssociationTargetClass($identifierField))
189+
);
190+
}
150191
}

tests/Doctrine/Tests/ORM/Hydration/ResultSetMappingTest.php

Lines changed: 0 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,6 @@ public function testBasicResultSetMapping()
6868
$this->assertEquals('status', $this->_rsm->getFieldName('status'));
6969
$this->assertEquals('username', $this->_rsm->getFieldName('username'));
7070
$this->assertEquals('name', $this->_rsm->getFieldName('name'));
71-
$this->assertSame('integer', $this->_rsm->getTypeOfSelectionRootSingleIdentifierColumn($this->_em));
7271
}
7372

7473
/**
@@ -98,7 +97,6 @@ public function testFluentInterface()
9897
$this->assertTrue($rms->isRelation('p'));
9998
$this->assertTrue($rms->hasParentAlias('p'));
10099
$this->assertTrue($rms->isMixedResult());
101-
$this->assertSame('integer', $this->_rsm->getTypeOfSelectionRootSingleIdentifierColumn($this->_em));
102100
}
103101

104102
/**
@@ -275,26 +273,6 @@ public function testAddNamedNativeQueryResultClass()
275273
$this->assertEquals(CmsUser::class, $rsm->getDeclaringClass('username'));
276274
}
277275

278-
public function testIdentifierTypeForScalarExpression() : void
279-
{
280-
$rsm = (new Parser($this->_em->createQuery('SELECT e.id4 FROM ' . AuxiliaryEntity::class . ' e')))
281-
->parse()
282-
->getResultSetMapping();
283-
284-
self::assertNotNull($rsm);
285-
self::assertSame('rot13', $rsm->getTypeOfSelectionRootSingleIdentifierColumn($this->_em));
286-
}
287-
288-
public function testIdentifierTypeForRootEntityColumnThatHasAssociationAsIdentifier() : void
289-
{
290-
$rsm = (new Parser($this->_em->createQuery('SELECT e FROM ' . OwningManyToOneIdForeignKeyEntity::class . ' e')))
291-
->parse()
292-
->getResultSetMapping();
293-
294-
self::assertNotNull($rsm);
295-
self::assertSame('rot13', $rsm->getTypeOfSelectionRootSingleIdentifierColumn($this->_em));
296-
}
297-
298276
/**
299277
* @group DDC-117
300278
*/

tests/Doctrine/Tests/ORM/Tools/Pagination/PaginationTestCase.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ abstract class PaginationTestCase extends OrmTestCase
1111
*/
1212
public $entityManager;
1313

14-
public function setUp()
14+
protected function setUp()
1515
{
1616
$this->entityManager = $this->_getTestEntityManager();
1717
}

0 commit comments

Comments
 (0)