diff --git a/lib/Doctrine/ORM/Internal/Hydration/AbstractHydrator.php b/lib/Doctrine/ORM/Internal/Hydration/AbstractHydrator.php index 5fe4fa1e53d..8a9c0f4d7e4 100644 --- a/lib/Doctrine/ORM/Internal/Hydration/AbstractHydrator.php +++ b/lib/Doctrine/ORM/Internal/Hydration/AbstractHydrator.php @@ -23,6 +23,7 @@ use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\Events; use Doctrine\ORM\Mapping\ClassMetadata; +use Doctrine\ORM\Tools\Pagination\LimitSubqueryWalker; use PDO; use function array_map; use function in_array; @@ -351,13 +352,11 @@ protected function gatherScalarRowData(&$data) // WARNING: BC break! We know this is the desired behavior to type convert values, but this // erroneous behavior exists since 2.0 and we're forced to keep compatibility. - if ( ! isset($cacheKeyInfo['isScalar'])) { - $dqlAlias = $cacheKeyInfo['dqlAlias']; - $type = $cacheKeyInfo['type']; - $fieldName = $dqlAlias . '_' . $fieldName; - $value = $type - ? $type->convertToPHPValue($value, $this->_platform) - : $value; + if (! isset($cacheKeyInfo['isScalar'])) { + $type = $cacheKeyInfo['type']; + $value = $type ? $type->convertToPHPValue($value, $this->_platform) : $value; + + $fieldName = $cacheKeyInfo['dqlAlias'] . '_' . $fieldName; } $rowData[$fieldName] = $value; @@ -422,6 +421,12 @@ protected function hydrateColumnInfo($key) 'class' => new \ReflectionClass($mapping['className']), ]; + case isset($this->_rsm->scalarMappings[$key], $this->_hints[LimitSubqueryWalker::FORCE_DBAL_TYPE_CONVERSION]): + return $this->_cache[$key] = [ + 'fieldName' => $this->_rsm->scalarMappings[$key], + 'type' => Type::getType($this->_rsm->typeMappings[$key]), + 'dqlAlias' => '', + ]; case (isset($this->_rsm->scalarMappings[$key])): return $this->_cache[$key] = [ 'isScalar' => true, diff --git a/lib/Doctrine/ORM/Tools/Pagination/LimitSubqueryWalker.php b/lib/Doctrine/ORM/Tools/Pagination/LimitSubqueryWalker.php index f1f06e9e129..5614138a86c 100644 --- a/lib/Doctrine/ORM/Tools/Pagination/LimitSubqueryWalker.php +++ b/lib/Doctrine/ORM/Tools/Pagination/LimitSubqueryWalker.php @@ -39,10 +39,9 @@ */ class LimitSubqueryWalker extends TreeWalkerAdapter { - /** - * ID type hint. - */ - const IDENTIFIER_TYPE = 'doctrine_paginator.id.type'; + public const IDENTIFIER_TYPE = 'doctrine_paginator.id.type'; + + public const FORCE_DBAL_TYPE_CONVERSION = 'doctrine_paginator.scalar_result.force_dbal_type_conversion'; /** * Counter for generating unique order column aliases. @@ -82,6 +81,8 @@ public function walkSelectStatement(SelectStatement $AST) Type::getType($rootClass->fieldMappings[$identifier]['type']) ); + $this->_getQuery()->setHint(self::FORCE_DBAL_TYPE_CONVERSION, true); + $pathExpression = new PathExpression( PathExpression::TYPE_STATE_FIELD | PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION, $rootAlias, diff --git a/tests/Doctrine/Tests/ORM/Functional/PaginationTest.php b/tests/Doctrine/Tests/ORM/Functional/PaginationTest.php index 58a88dbd764..9f200cb8e9d 100644 --- a/tests/Doctrine/Tests/ORM/Functional/PaginationTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/PaginationTest.php @@ -2,19 +2,24 @@ namespace Doctrine\Tests\ORM\Functional; +use Doctrine\DBAL\Types\Type as DBALType; use Doctrine\ORM\Query; use Doctrine\ORM\Tools\Pagination\Paginator; +use Doctrine\Tests\DbalTypes\CustomIdObject; +use Doctrine\Tests\DbalTypes\CustomIdObjectType; use Doctrine\Tests\Models\CMS\CmsArticle; use Doctrine\Tests\Models\CMS\CmsEmail; use Doctrine\Tests\Models\CMS\CmsGroup; use Doctrine\Tests\Models\CMS\CmsUser; use Doctrine\Tests\Models\Company\CompanyManager; +use Doctrine\Tests\Models\CustomType\CustomIdObjectTypeParent; use Doctrine\Tests\Models\Pagination\Company; use Doctrine\Tests\Models\Pagination\Department; use Doctrine\Tests\Models\Pagination\Logo; use Doctrine\Tests\Models\Pagination\User1; use Doctrine\Tests\OrmFunctionalTestCase; use ReflectionMethod; +use function iterator_to_array; /** * @group DDC-1613 @@ -26,6 +31,14 @@ protected function setUp() $this->useModelSet('cms'); $this->useModelSet('pagination'); $this->useModelSet('company'); + $this->useModelSet('custom_id_object_type'); + + if (DBALType::hasType(CustomIdObjectType::NAME)) { + DBALType::overrideType(CustomIdObjectType::NAME, CustomIdObjectType::class); + } else { + DBALType::addType(CustomIdObjectType::NAME, CustomIdObjectType::class); + } + parent::setUp(); $this->populate(); } @@ -641,6 +654,27 @@ public function testQueryWalkerIsKept() $this->assertEquals(1, $paginator->count()); } + /** + * @group GH-7890 + */ + public function testCustomIdTypeWithoutOutputWalker() + { + $this->_em->persist(new CustomIdObjectTypeParent(new CustomIdObject('foo'))); + $this->_em->flush(); + + $dql = 'SELECT p FROM Doctrine\Tests\Models\CustomType\CustomIdObjectTypeParent p'; + $query = $this->_em->createQuery($dql); + + $paginator = new Paginator($query, true); + $paginator->setUseOutputWalkers(false); + + $matchedItems = iterator_to_array($paginator->getIterator()); + + self::assertCount(1, $matchedItems); + self::assertInstanceOf(CustomIdObjectTypeParent::class, $matchedItems[0]); + self::assertSame('foo', (string) $matchedItems[0]->id); + } + public function testCountQueryStripsParametersInSelect() { $query = $this->_em->createQuery(