From 8466060797f0f9c85dd8f9d6b36906c4cbf2e8f7 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Sun, 16 Oct 2011 19:23:20 +0200 Subject: [PATCH] DDC-1385 - Add INDEX BY scalar variables on the top-level --- .../ORM/Internal/Hydration/ArrayHydrator.php | 18 ++++++---- .../ORM/Internal/Hydration/ObjectHydrator.php | 17 +++++---- lib/Doctrine/ORM/Query/ResultSetMapping.php | 36 ++++++++++++++++++- lib/Doctrine/ORM/Query/SqlWalker.php | 25 ++++++++++--- .../ORM/Hydration/ObjectHydratorTest.php | 28 +++++++++++++++ 5 files changed, 104 insertions(+), 20 deletions(-) diff --git a/lib/Doctrine/ORM/Internal/Hydration/ArrayHydrator.php b/lib/Doctrine/ORM/Internal/Hydration/ArrayHydrator.php index cd30a37c0..cbc77d2d2 100644 --- a/lib/Doctrine/ORM/Internal/Hydration/ArrayHydrator.php +++ b/lib/Doctrine/ORM/Internal/Hydration/ArrayHydrator.php @@ -66,12 +66,12 @@ class ArrayHydrator extends AbstractHydrator } /** @override */ - protected function _hydrateRow(array $data, array &$cache, array &$result) + protected function _hydrateRow(array $row, array &$cache, array &$result) { // 1) Initialize $id = $this->_idTemplate; // initialize the id-memory $nonemptyComponents = array(); - $rowData = $this->_gatherRowData($data, $cache, $id, $nonemptyComponents); + $rowData = $this->_gatherRowData($row, $cache, $id, $nonemptyComponents); // Extract scalar values. They're appended at the end. if (isset($rowData['scalars'])) { @@ -128,8 +128,7 @@ class ArrayHydrator extends AbstractHydrator if ( ! $indexExists || ! $indexIsValid) { $element = $data; if (isset($this->_rsm->indexByMap[$dqlAlias])) { - $field = $this->_rsm->indexByMap[$dqlAlias]; - $baseElement[$relationAlias][$element[$field]] = $element; + $baseElement[$relationAlias][$row[$this->_rsm->indexByMap[$dqlAlias]]] = $element; } else { $baseElement[$relationAlias][] = $element; } @@ -167,6 +166,7 @@ class ArrayHydrator extends AbstractHydrator } else { $result[] = null; } + $resultKey = $this->_resultCounter; ++$this->_resultCounter; continue; } @@ -179,8 +179,7 @@ class ArrayHydrator extends AbstractHydrator } if (isset($this->_rsm->indexByMap[$dqlAlias])) { - $field = $this->_rsm->indexByMap[$dqlAlias]; - $resultKey = $rowData[$dqlAlias][$field]; + $resultKey = $row[$this->_rsm->indexByMap[$dqlAlias]]; $result[$resultKey] = $element; } else { $resultKey = $this->_resultCounter; @@ -204,7 +203,12 @@ class ArrayHydrator extends AbstractHydrator // Append scalar values to mixed result sets if (isset($scalars)) { if ( ! isset($resultKey) ) { - $resultKey = $this->_resultCounter - 1; + // this only ever happens when no object is fetched (scalar result only) + if (isset($this->_rsm->indexByMap['scalars'])) { + $resultKey = $row[$this->_rsm->indexByMap['scalars']]; + } else { + $resultKey = $this->_resultCounter - 1; + } } foreach ($scalars as $name => $value) { diff --git a/lib/Doctrine/ORM/Internal/Hydration/ObjectHydrator.php b/lib/Doctrine/ORM/Internal/Hydration/ObjectHydrator.php index 69b441480..4cbba6bb7 100644 --- a/lib/Doctrine/ORM/Internal/Hydration/ObjectHydrator.php +++ b/lib/Doctrine/ORM/Internal/Hydration/ObjectHydrator.php @@ -273,13 +273,13 @@ class ObjectHydrator extends AbstractHydrator * @param array $cache The cache to use. * @param array $result The result array to fill. */ - protected function _hydrateRow(array $data, array &$cache, array &$result) + protected function _hydrateRow(array $row, array &$cache, array &$result) { // Initialize $id = $this->_idTemplate; // initialize the id-memory $nonemptyComponents = array(); // Split the row data into chunks of class data. - $rowData = $this->_gatherRowData($data, $cache, $id, $nonemptyComponents); + $rowData = $this->_gatherRowData($row, $cache, $id, $nonemptyComponents); // Extract scalar values. They're appended at the end. if (isset($rowData['scalars'])) { @@ -352,8 +352,7 @@ class ObjectHydrator extends AbstractHydrator $element = $this->_getEntity($data, $dqlAlias); if (isset($this->_rsm->indexByMap[$dqlAlias])) { - $field = $this->_rsm->indexByMap[$dqlAlias]; - $indexValue = $this->_ce[$entityName]->reflFields[$field]->getValue($element); + $indexValue = $row[$this->_rsm->indexByMap[$dqlAlias]]; $reflFieldValue->hydrateSet($indexValue, $element); $this->_identifierMap[$path][$id[$parentAlias]][$id[$dqlAlias]] = $indexValue; } else { @@ -421,6 +420,7 @@ class ObjectHydrator extends AbstractHydrator } else { $result[] = null; } + $resultKey = $this->_resultCounter; ++$this->_resultCounter; continue; } @@ -433,8 +433,7 @@ class ObjectHydrator extends AbstractHydrator } if (isset($this->_rsm->indexByMap[$dqlAlias])) { - $field = $this->_rsm->indexByMap[$dqlAlias]; - $resultKey = $rowData[$dqlAlias][$field]; + $resultKey = $row[$this->_rsm->indexByMap[$dqlAlias]]; if (isset($this->_hints['collection'])) { $this->_hints['collection']->hydrateSet($resultKey, $element); @@ -473,7 +472,11 @@ class ObjectHydrator extends AbstractHydrator // Append scalar values to mixed result sets if (isset($scalars)) { if ( ! isset($resultKey) ) { - $resultKey = $this->_resultCounter - 1; + if (isset($this->_rsm->indexByMap['scalars'])) { + $resultKey = $row[$this->_rsm->indexByMap['scalars']]; + } else { + $resultKey = $this->_resultCounter - 1; + } } foreach ($scalars as $name => $value) { diff --git a/lib/Doctrine/ORM/Query/ResultSetMapping.php b/lib/Doctrine/ORM/Query/ResultSetMapping.php index 3a086e2cd..fcfc9b834 100644 --- a/lib/Doctrine/ORM/Query/ResultSetMapping.php +++ b/lib/Doctrine/ORM/Query/ResultSetMapping.php @@ -157,7 +157,41 @@ class ResultSetMapping */ public function addIndexBy($alias, $fieldName) { - $this->indexByMap[$alias] = $fieldName; + $found = false; + foreach ($this->fieldMappings AS $columnName => $columnFieldName) { + if ($columnFieldName === $fieldName && $this->columnOwnerMap[$columnName] == $alias) { + $this->addIndexByColumn($alias, $columnName); + $found = true; + break; + } + } + if (!$found) { + throw new \LogicException("Cannot add index by for dql alias " . $alias . " and field " . + $fieldName . " without calling addFieldResult() for them before."); + } + } + + /** + * Set to index by a scalar result column name + * + * @param $resultColumnName + * @return void + */ + public function addIndexByScalar($resultColumnName) + { + $this->indexByMap['scalars'] = $resultColumnName; + } + + /** + * Sets a column to use for indexing an entity or joined entity result by the given alias name. + * + * @param $alias + * @param $resultColumnName + * @return void + */ + public function addIndexByColumn($alias, $resultColumnName) + { + $this->indexByMap[$alias] = $resultColumnName; } /** diff --git a/lib/Doctrine/ORM/Query/SqlWalker.php b/lib/Doctrine/ORM/Query/SqlWalker.php index 188350197..e98e0db31 100644 --- a/lib/Doctrine/ORM/Query/SqlWalker.php +++ b/lib/Doctrine/ORM/Query/SqlWalker.php @@ -71,6 +71,13 @@ class SqlWalker implements TreeWalker /** Map from result variable names to their SQL column alias names. */ private $_scalarResultAliasMap = array(); + /** + * Map from DQL-Alias + Field-Name to SQL Column Alias + * + * @var array + */ + private $_scalarFields = array(); + /** Map of all components/classes that appear in the DQL query. */ private $_queryComponents; @@ -381,7 +388,7 @@ class SqlWalker implements TreeWalker } else if ($lockMode == LockMode::OPTIMISTIC) { foreach ($this->_selectedClasses AS $class) { if ( ! $class->isVersioned) { - throw \Doctrine\ORM\OptimisticLockException::lockFailed(); + throw \Doctrine\ORM\OptimisticLockException::lockFailed($class->name); } } } @@ -619,10 +626,17 @@ class SqlWalker implements TreeWalker } if ($identificationVariableDecl->indexBy) { - $this->_rsm->addIndexBy( - $identificationVariableDecl->indexBy->simpleStateFieldPathExpression->identificationVariable, - $identificationVariableDecl->indexBy->simpleStateFieldPathExpression->field - ); + $alias = $identificationVariableDecl->indexBy->simpleStateFieldPathExpression->identificationVariable; + $field = $identificationVariableDecl->indexBy->simpleStateFieldPathExpression->field; + + if (isset($this->_scalarFields[$alias][$field])) { + $this->_rsm->addIndexByScalar($this->_scalarFields[$alias][$field]); + } else { + $this->_rsm->addIndexBy( + $identificationVariableDecl->indexBy->simpleStateFieldPathExpression->identificationVariable, + $identificationVariableDecl->indexBy->simpleStateFieldPathExpression->field + ); + } } $sqlParts[] = $this->_platform->appendLockHint($sql, $this->_query->getHint(Query::HINT_LOCK_MODE)); @@ -1010,6 +1024,7 @@ class SqlWalker implements TreeWalker if ( ! $hidden) { $this->_rsm->addScalarResult($columnAlias, $resultAlias); + $this->_scalarFields[$dqlAlias][$fieldName] = $columnAlias; } } else if ($expr instanceof AST\AggregateExpression) { if ( ! $selectExpression->fieldIdentificationVariable) { diff --git a/tests/Doctrine/Tests/ORM/Hydration/ObjectHydratorTest.php b/tests/Doctrine/Tests/ORM/Hydration/ObjectHydratorTest.php index d2d6f24ce..65cd57c95 100644 --- a/tests/Doctrine/Tests/ORM/Hydration/ObjectHydratorTest.php +++ b/tests/Doctrine/Tests/ORM/Hydration/ObjectHydratorTest.php @@ -1208,4 +1208,32 @@ class ObjectHydratorTest extends HydrationTestCase $this->assertTrue(isset($result[2])); $this->assertEquals(2, $result[2][0]->id); } + + /** + * @group DDC-1385 + */ + public function testIndexByScalarsOnly() + { + $rsm = new ResultSetMapping; + $rsm->addScalarResult('sclr0', 'nameUpper'); + $rsm->addIndexByScalar('sclr0'); + + // Faked result set + $resultSet = array( + //row1 + array( + 'sclr0' => 'ROMANB', + ), + array( + 'sclr0' => 'JWAGE', + ), + ); + + $stmt = new HydratorMockStatement($resultSet); + $hydrator = new \Doctrine\ORM\Internal\Hydration\ObjectHydrator($this->_em); + + $result = $hydrator->hydrateAll($stmt, $rsm, array(Query::HINT_FORCE_PARTIAL_LOAD => true)); + + $this->assertEquals(array('ROMANB' => array('nameUpper' => 'ROMANB'), 'JWAGE' => array('nameUpper' => 'JWAGE')), $result); + } }