diff --git a/lib/Doctrine/ORM/Internal/Hydration/SingleScalarHydrator.php b/lib/Doctrine/ORM/Internal/Hydration/SingleScalarHydrator.php index 621e01d8407..b595909227f 100644 --- a/lib/Doctrine/ORM/Internal/Hydration/SingleScalarHydrator.php +++ b/lib/Doctrine/ORM/Internal/Hydration/SingleScalarHydrator.php @@ -32,12 +32,12 @@ protected function hydrateAllData() throw new NonUniqueResultException('The query returned multiple rows. Change the query or use a different result function like getScalarResult().'); } - if (count($data[key($data)]) > 1) { + $result = $this->gatherScalarRowData($data[key($data)]); + + if (count($result) > 1) { throw new NonUniqueResultException('The query returned a row containing multiple columns. Change the query or use a different result function like getScalarResult().'); } - $result = $this->gatherScalarRowData($data[key($data)]); - return array_shift($result); } } diff --git a/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7512Test.php b/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7512Test.php index 9f74a500679..b19ab32d56d 100644 --- a/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7512Test.php +++ b/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7512Test.php @@ -16,7 +16,7 @@ class GH7512Test extends OrmFunctionalTestCase { - protected function setUp() : void + protected function setUp(): void { parent::setUp(); @@ -32,7 +32,7 @@ protected function setUp() : void $this->_em->clear(); } - public function testFindEntityByAssociationPropertyJoinedChildWithClearMetadata() : void + public function testFindEntityByAssociationPropertyJoinedChildWithClearMetadata(): void { // unset metadata for entity B as though it hasn't been touched yet in application lifecycle. $this->_em->getMetadataFactory()->setMetadataFor(GH7512EntityB::class, null); diff --git a/tests/Doctrine/Tests/ORM/Hydration/SingleScalarHydratorTest.php b/tests/Doctrine/Tests/ORM/Hydration/SingleScalarHydratorTest.php index a2d55e55094..148deb09244 100644 --- a/tests/Doctrine/Tests/ORM/Hydration/SingleScalarHydratorTest.php +++ b/tests/Doctrine/Tests/ORM/Hydration/SingleScalarHydratorTest.php @@ -14,51 +14,127 @@ class SingleScalarHydratorTest extends HydrationTestCase { - /** Result set provider for the HYDRATE_SINGLE_SCALAR tests */ - public static function singleScalarResultSetProvider(): array + public static function validResultSetProvider() { - return [ - // valid - 'valid' => [ - 'name' => 'result1', - 'resultSet' => [ - ['u__name' => 'romanb'], + // SELECT u.name FROM CmsUser u WHERE u.id = 1 + yield [ + [ + ['u__name' => 'romanb'], + ], + 'romanb', + ]; + + // SELECT u.id FROM CmsUser u WHERE u.id = 1 + yield [ + [ + ['u__id' => '1'], + ], + 1, + ]; + + // SELECT + // u.id, + // COUNT(u.postsCount + u.likesCount) AS HIDDEN score + // FROM CmsUser u + // WHERE u.id = 1 + yield [ + [ + [ + 'u__id' => '1', + 'score' => 10, // Ignored since not part of ResultSetMapping (cf. HIDDEN keyword) ], ], - // valid + 1, + ]; + } + + /** + * @dataProvider validResultSetProvider + */ + public function testHydrateSingleScalarFromFieldMappingWithValidResultSet(array $resultSet, $expectedResult): void + { + $rsm = new ResultSetMapping(); + $rsm->addEntityResult(CmsUser::class, 'u'); + $rsm->addFieldResult('u', 'u__id', 'id'); + $rsm->addFieldResult('u', 'u__name', 'name'); + + $stmt = ArrayResultFactory::createFromArray($resultSet); + $hydrator = new SingleScalarHydrator($this->entityManager); + + $result = $hydrator->hydrateAll($stmt, $rsm); + $this->assertEquals($expectedResult, $result); + } + + /** + * @dataProvider validResultSetProvider + */ + public function testHydrateSingleScalarFromScalarMappingWithValidResultSet(array $resultSet, $expectedResult): void + { + $rsm = new ResultSetMapping(); + $rsm->addScalarResult('u__id', 'id', 'string'); + $rsm->addScalarResult('u__name', 'name', 'string'); + + $stmt = ArrayResultFactory::createFromArray($resultSet); + $hydrator = new SingleScalarHydrator($this->entityManager); + + $result = $hydrator->hydrateAll($stmt, $rsm); + $this->assertEquals($expectedResult, $result); + } + + public static function invalidResultSetProvider() + { + // Single row (OK), multiple columns (NOT OK) + yield [ [ - 'name' => 'result2', - 'resultSet' => [ - ['u__id' => '1'], + [ + 'u__id' => '1', + 'u__name' => 'romanb', ], ], - // invalid + ]; + + // Multiple rows (NOT OK), single column (OK) + yield [ [ - 'name' => 'result3', - 'resultSet' => [ - [ - 'u__id' => '1', - 'u__name' => 'romanb', - ], + ['u__id' => '1'], + ['u__id' => '2'], + ], + ]; + + // Multiple rows (NOT OK), single column with HIDDEN result (OK) + yield [ + [ + [ + 'u__id' => '1', + 'score' => 10, // Ignored since not part of ResultSetMapping + ], + [ + 'u__id' => '2', + 'score' => 10, // Ignored since not part of ResultSetMapping ], ], - // invalid + 1, + ]; + + // Multiple row (NOT OK), multiple columns (NOT OK) + yield [ [ - 'name' => 'result4', - 'resultSet' => [ - ['u__id' => '1'], - ['u__id' => '2'], + [ + 'u__id' => '1', + 'u__name' => 'romanb', + ], + [ + 'u__id' => '2', + 'u__name' => 'romanb', ], ], ]; } /** - * select u.name from CmsUser u where u.id = 1 - * - * @dataProvider singleScalarResultSetProvider + * @dataProvider invalidResultSetProvider */ - public function testHydrateSingleScalar($name, $resultSet): void + public function testHydrateSingleScalarFromFieldMappingWithInvalidResultSet(array $resultSet): void { $rsm = new ResultSetMapping(); $rsm->addEntityResult(CmsUser::class, 'u'); @@ -68,23 +144,23 @@ public function testHydrateSingleScalar($name, $resultSet): void $stmt = ArrayResultFactory::createFromArray($resultSet); $hydrator = new SingleScalarHydrator($this->entityManager); - if ($name === 'result1') { - $result = $hydrator->hydrateAll($stmt, $rsm); - self::assertEquals('romanb', $result); - - return; - } + $this->expectException(NonUniqueResultException::class); + $hydrator->hydrateAll($stmt, $rsm); + } - if ($name === 'result2') { - $result = $hydrator->hydrateAll($stmt, $rsm); - self::assertEquals(1, $result); + /** + * @dataProvider invalidResultSetProvider + */ + public function testHydrateSingleScalarFromScalarMappingWithInvalidResultSet(array $resultSet): void + { + $rsm = new ResultSetMapping(); + $rsm->addScalarResult('u__id', 'id', 'string'); + $rsm->addScalarResult('u__name', 'name', 'string'); - return; - } + $stmt = ArrayResultFactory::createFromArray($resultSet); + $hydrator = new SingleScalarHydrator($this->entityManager); - if (in_array($name, ['result3', 'result4'], true)) { - $this->expectException(NonUniqueResultException::class); - $hydrator->hydrateAll($stmt, $rsm); - } + $this->expectException(NonUniqueResultException::class); + $hydrator->hydrateAll($stmt, $rsm); } }