From 9692fc8c5b8a6e913a723063561183d101c8c695 Mon Sep 17 00:00:00 2001 From: Guilherme Blanco Date: Thu, 24 Apr 2014 04:47:13 +0000 Subject: [PATCH] Fixed issue if aliases were specific names, you would either get completely misleading results (dql alias: scalars) or a fatal error (dql alias: newObjects). --- .../Internal/Hydration/AbstractHydrator.php | 10 ++-- .../ORM/Internal/Hydration/ArrayHydrator.php | 53 +++++------------ .../ORM/Internal/Hydration/ObjectHydrator.php | 21 +++---- .../Tests/ORM/Hydration/ArrayHydratorTest.php | 57 +++++++++++++++++++ 4 files changed, 88 insertions(+), 53 deletions(-) diff --git a/lib/Doctrine/ORM/Internal/Hydration/AbstractHydrator.php b/lib/Doctrine/ORM/Internal/Hydration/AbstractHydrator.php index 31b1bce48..a62e107ff 100644 --- a/lib/Doctrine/ORM/Internal/Hydration/AbstractHydrator.php +++ b/lib/Doctrine/ORM/Internal/Hydration/AbstractHydrator.php @@ -247,7 +247,7 @@ abstract class AbstractHydrator */ protected function gatherRowData(array $data, array &$cache, array &$id, array &$nonemptyComponents) { - $rowData = array(); + $rowData = array('data' => array()); foreach ($data as $key => $value) { $cacheKeyInfo = $this->getColumnCacheInfo($key, $cache); @@ -281,7 +281,7 @@ abstract class AbstractHydrator $fieldName = $cacheKeyInfo['fieldName']; // Avoid double setting or null assignment - if (isset($rowData[$dqlAlias][$fieldName]) || $value === null) { + if (isset($rowData['data'][$dqlAlias][$fieldName]) || $value === null) { break; } @@ -290,7 +290,7 @@ abstract class AbstractHydrator $nonemptyComponents[$dqlAlias] = true; } - $rowData[$dqlAlias][$fieldName] = $value; + $rowData['data'][$dqlAlias][$fieldName] = $value; break; default: @@ -301,7 +301,7 @@ abstract class AbstractHydrator // in an inheritance hierarchy the same field could be defined several times. // We overwrite this value so long we don't have a non-null value, that value we keep. // Per definition it cannot be that a field is defined several times and has several values. - if (isset($rowData[$dqlAlias][$fieldName]) && $value === null) { + if (isset($rowData['data'][$dqlAlias][$fieldName]) && $value === null) { break; } @@ -315,7 +315,7 @@ abstract class AbstractHydrator $nonemptyComponents[$dqlAlias] = true; } - $rowData[$dqlAlias][$fieldName] = $value; + $rowData['data'][$dqlAlias][$fieldName] = $value; break; } } diff --git a/lib/Doctrine/ORM/Internal/Hydration/ArrayHydrator.php b/lib/Doctrine/ORM/Internal/Hydration/ArrayHydrator.php index 8adf2f0ed..f6cab279d 100644 --- a/lib/Doctrine/ORM/Internal/Hydration/ArrayHydrator.php +++ b/lib/Doctrine/ORM/Internal/Hydration/ArrayHydrator.php @@ -110,30 +110,8 @@ class ArrayHydrator extends AbstractHydrator $nonemptyComponents = array(); $rowData = $this->gatherRowData($row, $cache, $id, $nonemptyComponents); - // Extract scalar values. They're appended at the end. - if (isset($rowData['scalars'])) { - $scalars = $rowData['scalars']; - - unset($rowData['scalars']); - - if (empty($rowData)) { - ++$this->_resultCounter; - } - } - - // Extract "new" object constructor arguments. They're appended at the end. - if (isset($rowData['newObjects'])) { - $newObjects = $rowData['newObjects']; - - unset($rowData['newObjects']); - - if (empty($rowData)) { - ++$this->_resultCounter; - } - } - // 2) Now hydrate the data found in the current row. - foreach ($rowData as $dqlAlias => $data) { + foreach ($rowData['data'] as $dqlAlias => $data) { $index = false; if (isset($this->_rsm->parentAliasMap[$dqlAlias])) { @@ -232,8 +210,8 @@ class ArrayHydrator extends AbstractHydrator // Check for an existing element if ($this->_isSimpleQuery || ! isset($this->_identifierMap[$dqlAlias][$id[$dqlAlias]])) { $element = $this->_rsm->isMixed - ? array($entityKey => $rowData[$dqlAlias]) - : $rowData[$dqlAlias]; + ? array($entityKey => $data) + : $data; if (isset($this->_rsm->indexByMap[$dqlAlias])) { $resultKey = $row[$this->_rsm->indexByMap[$dqlAlias]]; @@ -249,40 +227,39 @@ class ArrayHydrator extends AbstractHydrator } else { $index = $this->_identifierMap[$dqlAlias][$id[$dqlAlias]]; $resultKey = $index; - - /*if ($this->_rsm->isMixed) { - $result[] =& $result[$index]; - ++$this->_resultCounter; - }*/ } $this->updateResultPointer($result, $index, $dqlAlias, false); } } + if ( ! isset($resultKey)) { + $this->_resultCounter++; + } + // Append scalar values to mixed result sets - if (isset($scalars)) { - if ( ! isset($resultKey) ) { + if (isset($rowData['scalars'])) { + if ( ! isset($resultKey)) { // this only ever happens when no object is fetched (scalar result only) $resultKey = isset($this->_rsm->indexByMap['scalars']) ? $row[$this->_rsm->indexByMap['scalars']] : $this->_resultCounter - 1; } - foreach ($scalars as $name => $value) { + foreach ($rowData['scalars'] as $name => $value) { $result[$resultKey][$name] = $value; } } - + // Append new object to mixed result sets - if (isset($newObjects)) { - if ( ! isset($resultKey) ) { + if (isset($rowData['newObjects'])) { + if ( ! isset($resultKey)) { $resultKey = $this->_resultCounter - 1; } - $count = count($newObjects); + $count = count($rowData['newObjects']); - foreach ($newObjects as $objIndex => $newObject) { + foreach ($rowData['newObjects'] as $objIndex => $newObject) { $class = $newObject['class']; $args = $newObject['args']; $obj = $class->newInstanceArgs($args); diff --git a/lib/Doctrine/ORM/Internal/Hydration/ObjectHydrator.php b/lib/Doctrine/ORM/Internal/Hydration/ObjectHydrator.php index c17f91ae8..473da1b99 100644 --- a/lib/Doctrine/ORM/Internal/Hydration/ObjectHydrator.php +++ b/lib/Doctrine/ORM/Internal/Hydration/ObjectHydrator.php @@ -387,7 +387,7 @@ class ObjectHydrator extends AbstractHydrator } // Hydrate the data chunks - foreach ($rowData as $dqlAlias => $data) { + foreach ($rowData['data'] as $dqlAlias => $data) { $entityName = $this->_rsm->aliasMap[$dqlAlias]; if (isset($this->_rsm->parentAliasMap[$dqlAlias])) { @@ -408,7 +408,7 @@ class ObjectHydrator extends AbstractHydrator $relationField = $this->_rsm->relationMap[$dqlAlias]; $relation = $parentClass->associationMappings[$relationField]; $reflField = $parentClass->reflFields[$relationField]; - + // Get a reference to the parent object to which the joined element belongs. if ($this->_rsm->isMixed && isset($this->rootAliases[$parentAlias])) { $first = reset($this->resultPointers); @@ -418,16 +418,13 @@ class ObjectHydrator extends AbstractHydrator } else { // Parent object of relation not found, mark as not-fetched again $element = $this->getEntity($data, $dqlAlias); - + // Update result pointer and provide initial fetch data for parent $this->resultPointers[$dqlAlias] = $element; - $rowData[$parentAlias][$relationField] = $element; - + $rowData['data'][$parentAlias][$relationField] = $element; + // Mark as not-fetched again unset($this->_hints['fetched'][$parentAlias][$relationField]); - - ////unset($rowData[$dqlAlias]); - //$rowData[$dqlAlias] = $data; continue; } @@ -437,7 +434,7 @@ class ObjectHydrator extends AbstractHydrator if ( ! ($relation['type'] & ClassMetadata::TO_ONE)) { // PATH A: Collection-valued association $reflFieldValue = $reflField->getValue($parentObject); - + if (isset($nonemptyComponents[$dqlAlias])) { $collKey = $oid . $relationField; if (isset($this->initializedCollections[$collKey])) { @@ -486,7 +483,7 @@ class ObjectHydrator extends AbstractHydrator } else { // PATH B: Single-valued association $reflFieldValue = $reflField->getValue($parentObject); - + if ( ! $reflFieldValue || isset($this->_hints[Query::HINT_REFRESH]) || ($reflFieldValue instanceof Proxy && !$reflFieldValue->__isInitialized__)) { // we only need to take action if this value is null, // we refresh the entity or its an unitialized proxy. @@ -584,6 +581,10 @@ class ObjectHydrator extends AbstractHydrator } } + if ( ! isset($resultKey) ) { + $this->resultCounter++; + } + // Append scalar values to mixed result sets if (isset($scalars)) { if ( ! isset($resultKey) ) { diff --git a/tests/Doctrine/Tests/ORM/Hydration/ArrayHydratorTest.php b/tests/Doctrine/Tests/ORM/Hydration/ArrayHydratorTest.php index 3fa34975d..e56b188fb 100644 --- a/tests/Doctrine/Tests/ORM/Hydration/ArrayHydratorTest.php +++ b/tests/Doctrine/Tests/ORM/Hydration/ArrayHydratorTest.php @@ -12,6 +12,8 @@ class ArrayHydratorTest extends HydrationTestCase return array( array(0), array('user'), + array('scalars'), + array('newObjects'), ); } @@ -53,6 +55,61 @@ class ArrayHydratorTest extends HydrationTestCase $this->assertEquals('jwage', $result[1]['name']); } + /** + * SELECT PARTIAL scalars.{id, name}, UPPER(scalars.name) AS nameUpper + * FROM Doctrine\Tests\Models\CMS\CmsUser scalars + * + * @dataProvider provideDataForUserEntityResult + */ + public function testSimpleEntityWithScalarQuery($userEntityKey) + { + $alias = $userEntityKey ?: 'u'; + + $rsm = new ResultSetMapping; + $rsm->addEntityResult('Doctrine\Tests\Models\CMS\CmsUser', $alias); + $rsm->addFieldResult($alias, 's__id', 'id'); + $rsm->addFieldResult($alias, 's__name', 'name'); + $rsm->addScalarResult('sclr0', 'nameUpper'); + + // Faked result set + $resultSet = array( + array( + 's__id' => '1', + 's__name' => 'romanb', + 'sclr0' => 'ROMANB', + ), + array( + 's__id' => '2', + 's__name' => 'jwage', + 'sclr0' => 'JWAGE', + ) + ); + + $stmt = new HydratorMockStatement($resultSet); + $hydrator = new \Doctrine\ORM\Internal\Hydration\ArrayHydrator($this->_em); + $result = $hydrator->hydrateAll($stmt, $rsm); + + $this->assertEquals(2, count($result)); + + $this->assertTrue(is_array($result)); + + $this->assertArrayHasKey('nameUpper', $result[0]); + $this->assertArrayNotHasKey('id', $result[0]); + $this->assertArrayNotHasKey('name', $result[0]); + + $this->assertArrayHasKey(0, $result[0]); + $this->assertArrayHasKey('id', $result[0][0]); + $this->assertArrayHasKey('name', $result[0][0]); + + $this->assertArrayHasKey('nameUpper', $result[1]); + $this->assertArrayNotHasKey('id', $result[1]); + $this->assertArrayNotHasKey('name', $result[1]); + + $this->assertArrayHasKey(0, $result[1]); + $this->assertArrayHasKey('id', $result[1][0]); + $this->assertArrayHasKey('name', $result[1][0]); + } + /** * SELECT PARTIAL u.{id, name} AS user * FROM Doctrine\Tests\Models\CMS\CmsUser u