From e089921f8df34b066bf5f5238335772e8958878e Mon Sep 17 00:00:00 2001 From: Alexandre Quercia Date: Mon, 15 Apr 2024 20:09:25 +0200 Subject: [PATCH 01/37] fix: add failing test when aliasing a column of a joined relation --- tests/Ticket/GH134TestCase.php | 51 ++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 tests/Ticket/GH134TestCase.php diff --git a/tests/Ticket/GH134TestCase.php b/tests/Ticket/GH134TestCase.php new file mode 100644 index 000000000..290590877 --- /dev/null +++ b/tests/Ticket/GH134TestCase.php @@ -0,0 +1,51 @@ +select('u.id, e.address as aliasAddress') + ->from('User u') + ->innerJoin('u.Email e') + ; + + $results = $query->execute(array(), $hydrateType); + + $expectedSql = 'SELECT e.id AS e__id, e2.address AS e2__0 FROM entity e INNER JOIN email e2 ON e.email_id = e2.id WHERE (e.type = 0)'; + + $this->assertEqual($expectedSql, $query->getSqlQuery()); + $this->assertEqual($expectedKeys, array_keys($results[0])); + $this->assertEqual(count($this->users), count($results)); + + $this->pass(); + } catch (Exception $e) { + $this->fail($e->getMessage()); + } + } + + public function test_hydrateScalar_withAllColumnsAliased_thenResultsHasAllRecords() + { + $hydrateType = Doctrine_Core::HYDRATE_SCALAR; + $expectedKeys = array('u_id', 'e_aliasAddress'); + + $this->doTestWithAllColumnsAliased($hydrateType, $expectedKeys); + } + + public function test_hydrateArrayShallow_withAllColumnsAliased_thenResultsHasAllRecords() + { + $hydrateType = Doctrine_Core::HYDRATE_ARRAY_SHALLOW; + $expectedKeys = array('id', 'aliasAddress'); + + $this->doTestWithAllColumnsAliased($hydrateType, $expectedKeys); + } + + public function test_hydrateArray_withAllColumnsAliased_thenResultsHasAllRecords() + { + $hydrateType = Doctrine_Core::HYDRATE_ARRAY; + $expectedKeys = array('id', 'aliasAddress'); + + $this->doTestWithAllColumnsAliased($hydrateType, $expectedKeys); + } +} From 6a0fe338a205a3ea8cdae3bbb83bce799340e4ce Mon Sep 17 00:00:00 2001 From: Alexandre Quercia Date: Mon, 15 Apr 2024 20:37:15 +0200 Subject: [PATCH 02/37] fixup! fix: add failing test when aliasing a column of a joined relation --- tests/Ticket/585TestCase.php | 2 +- tests/Ticket/GH134TestCase.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/Ticket/585TestCase.php b/tests/Ticket/585TestCase.php index 9c2ed8f5c..f75631d7a 100644 --- a/tests/Ticket/585TestCase.php +++ b/tests/Ticket/585TestCase.php @@ -63,7 +63,7 @@ public function test_hydrateArrayShallow_withAllColumnsAliased_thenResultsHasAll public function test_hydrateArray_withAllColumnsAliased_thenResultsHasAllRecords() { $hydrateType = Doctrine_Core::HYDRATE_ARRAY; - $expectedKeys = array('aliasId', 'aliasName'); + $expectedKeys = array('id', 'aliasId', 'aliasName'); $this->doTestWithAllColumnsAliased($hydrateType, $expectedKeys); } diff --git a/tests/Ticket/GH134TestCase.php b/tests/Ticket/GH134TestCase.php index 290590877..aba646514 100644 --- a/tests/Ticket/GH134TestCase.php +++ b/tests/Ticket/GH134TestCase.php @@ -44,7 +44,7 @@ public function test_hydrateArrayShallow_withAllColumnsAliased_thenResultsHasAll public function test_hydrateArray_withAllColumnsAliased_thenResultsHasAllRecords() { $hydrateType = Doctrine_Core::HYDRATE_ARRAY; - $expectedKeys = array('id', 'aliasAddress'); + $expectedKeys = array('id', 'aliasAddress', 'Email'); $this->doTestWithAllColumnsAliased($hydrateType, $expectedKeys); } From 0af3477e0e319fd7bc699adf83504decb59aafc2 Mon Sep 17 00:00:00 2001 From: Alexandre Quercia Date: Tue, 16 Apr 2024 01:09:40 +0200 Subject: [PATCH 03/37] fixup! fix: add failing test when aliasing a column of a joined relation --- lib/Doctrine/Hydrator/Graph.php | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/lib/Doctrine/Hydrator/Graph.php b/lib/Doctrine/Hydrator/Graph.php index b227f3874..c639fc1e3 100644 --- a/lib/Doctrine/Hydrator/Graph.php +++ b/lib/Doctrine/Hydrator/Graph.php @@ -311,6 +311,7 @@ protected function _gatherRowData(&$data, &$cache, &$id, &$nonemptyComponents) $table = $this->_queryComponents[$cache[$key]['dqlAlias']]['table']; $fieldName = $table->getFieldName($last); $cache[$key]['fieldName'] = $fieldName; + $cache[$key]['isRelation'] = isset($this->_queryComponents[$cache[$key]['dqlAlias']]['relation']); if (isset($this->_queryComponents[$cache[$key]['dqlAlias']]['agg_field'][$last])) { $fieldName = $this->_queryComponents[$cache[$key]['dqlAlias']]['agg_field'][$last]; @@ -351,11 +352,19 @@ protected function _gatherRowData(&$data, &$cache, &$id, &$nonemptyComponents) $preparedValue = $table->prepareValue($fieldName, $value, $cache[$key]['type']); } + // Ticket #1380 // Hydrate aggregates in to the root component as well. // So we know that all aggregate values will always be available in the root component if ($agg) { $rowData[$this->_rootAlias][$fieldName] = $preparedValue; + + if (!isset($rowData[$dqlAlias])) { + if ($cache[$key]['isRelation'] && $this instanceof Doctrine_Hydrator_ArrayDriver) { + $rowData[$dqlAlias] = array(); + } + } + if (isset($rowData[$dqlAlias])) { $rowData[$dqlAlias][$fieldName] = $preparedValue; } From 5572c06e27a2bc05e215ff682160c771c761cbea Mon Sep 17 00:00:00 2001 From: Alexandre Quercia Date: Tue, 16 Apr 2024 02:33:26 +0200 Subject: [PATCH 04/37] fixup! fix: add failing test when aliasing a column of a joined relation --- lib/Doctrine/Hydrator/Graph.php | 34 ++++++++++++++++++++++----------- 1 file changed, 23 insertions(+), 11 deletions(-) diff --git a/lib/Doctrine/Hydrator/Graph.php b/lib/Doctrine/Hydrator/Graph.php index c639fc1e3..e85cf1c71 100644 --- a/lib/Doctrine/Hydrator/Graph.php +++ b/lib/Doctrine/Hydrator/Graph.php @@ -311,13 +311,24 @@ protected function _gatherRowData(&$data, &$cache, &$id, &$nonemptyComponents) $table = $this->_queryComponents[$cache[$key]['dqlAlias']]['table']; $fieldName = $table->getFieldName($last); $cache[$key]['fieldName'] = $fieldName; + $cache[$key]['isRelation'] = isset($this->_queryComponents[$cache[$key]['dqlAlias']]['relation']); + if (isset($this->_queryComponents[$cache[$key]['dqlAlias']]['agg'][$fieldName])) { + $cache[$key]['isAgg'] = true; + $cache[$key]['aliasName'] = $this->_queryComponents[$cache[$key]['dqlAlias']]['agg'][$fieldName]; + } else { + $cache[$key]['isAgg'] = false; + $cache[$key]['aliasName'] = $fieldName; + } + if (isset($this->_queryComponents[$cache[$key]['dqlAlias']]['agg_field'][$last])) { - $fieldName = $this->_queryComponents[$cache[$key]['dqlAlias']]['agg_field'][$last]; + $cache[$key]['columnName'] = $this->_queryComponents[$cache[$key]['dqlAlias']]['agg_field'][$last]; + } else { + $cache[$key]['columnName'] = $fieldName; } - if ($table->isIdentifier($fieldName)) { + if ($table->isIdentifier($cache[$key]['columnName'])) { $cache[$key]['isIdentifier'] = true; } else { $cache[$key]['isIdentifier'] = false; @@ -335,12 +346,8 @@ protected function _gatherRowData(&$data, &$cache, &$id, &$nonemptyComponents) $map = $this->_queryComponents[$cache[$key]['dqlAlias']]; $table = $map['table']; $dqlAlias = $cache[$key]['dqlAlias']; - $fieldName = $cache[$key]['fieldName']; - $agg = false; - if (isset($this->_queryComponents[$dqlAlias]['agg'][$fieldName])) { - $fieldName = $this->_queryComponents[$dqlAlias]['agg'][$fieldName]; - $agg = true; - } + $fieldName = $cache[$key]['aliasName']; + $agg = $cache[$key]['isAgg']; if ($cache[$key]['isIdentifier']) { $id[$dqlAlias] .= '|' . $value; @@ -352,19 +359,24 @@ protected function _gatherRowData(&$data, &$cache, &$id, &$nonemptyComponents) $preparedValue = $table->prepareValue($fieldName, $value, $cache[$key]['type']); } - // Ticket #1380 // Hydrate aggregates in to the root component as well. // So we know that all aggregate values will always be available in the root component if ($agg) { - $rowData[$this->_rootAlias][$fieldName] = $preparedValue; - if (!isset($rowData[$dqlAlias])) { if ($cache[$key]['isRelation'] && $this instanceof Doctrine_Hydrator_ArrayDriver) { $rowData[$dqlAlias] = array(); } } + if ($this instanceof Doctrine_Hydrator_ArrayDriver) { + if ($cache[$key]['isIdentifier']) { + $rowData[$dqlAlias][$cache[$key]['columnName']] = $preparedValue; + } + } + + $rowData[$this->_rootAlias][$fieldName] = $preparedValue; + if (isset($rowData[$dqlAlias])) { $rowData[$dqlAlias][$fieldName] = $preparedValue; } From 296601802242dffdc006f0ede377eb4cd0287cd5 Mon Sep 17 00:00:00 2001 From: Alexandre Quercia Date: Tue, 16 Apr 2024 02:59:09 +0200 Subject: [PATCH 05/37] fixup! fix: add failing test when aliasing a column of a joined relation --- lib/Doctrine/Hydrator/Graph.php | 12 ++--- tests/Ticket/585bTestCase.php | 81 +++++++++++++++++++++++++++++++++ 2 files changed, 87 insertions(+), 6 deletions(-) create mode 100644 tests/Ticket/585bTestCase.php diff --git a/lib/Doctrine/Hydrator/Graph.php b/lib/Doctrine/Hydrator/Graph.php index e85cf1c71..15800ffe2 100644 --- a/lib/Doctrine/Hydrator/Graph.php +++ b/lib/Doctrine/Hydrator/Graph.php @@ -363,14 +363,14 @@ protected function _gatherRowData(&$data, &$cache, &$id, &$nonemptyComponents) // Hydrate aggregates in to the root component as well. // So we know that all aggregate values will always be available in the root component if ($agg) { - if (!isset($rowData[$dqlAlias])) { - if ($cache[$key]['isRelation'] && $this instanceof Doctrine_Hydrator_ArrayDriver) { - $rowData[$dqlAlias] = array(); + if ($this instanceof Doctrine_Hydrator_ArrayDriver) { + if (!isset($rowData[$dqlAlias])) { + if ($cache[$key]['isRelation']) { + $rowData[$dqlAlias] = array(); + } } - } - if ($this instanceof Doctrine_Hydrator_ArrayDriver) { - if ($cache[$key]['isIdentifier']) { + if ($cache[$key]['isIdentifier'] && !isset($rowData[$dqlAlias][$cache[$key]['columnName']])) { $rowData[$dqlAlias][$cache[$key]['columnName']] = $preparedValue; } } diff --git a/tests/Ticket/585bTestCase.php b/tests/Ticket/585bTestCase.php new file mode 100644 index 000000000..255311128 --- /dev/null +++ b/tests/Ticket/585bTestCase.php @@ -0,0 +1,81 @@ +. + */ + +class Doctrine_Ticket_585b_TestCase extends Doctrine_UnitTestCase +{ + private function doTestAliasWithSameNameAsIdentifiers($hydrateType, $expectedKeys, $expectedKeyValues = array()) + { + try { + $query = Doctrine_Query::create() + ->select('99 as id, u.id as aliasId') + ->from('User u') + ; + + $results = $query->execute(array(), $hydrateType); + + $expectedSql = 'SELECT 99 AS e__0, e.id AS e__1 FROM entity e WHERE (e.type = 0)'; + + $this->assertEqual($expectedSql, $query->getSqlQuery()); + $this->assertEqual($expectedKeys, array_keys($results[0])); + foreach ($expectedKeyValues as $key => $value) { + $this->assertEqual($value, $results[0][$key]); + } + $this->assertEqual(count($this->users), count($results)); + + $this->pass(); + } catch (Exception $e) { + $this->fail($e->getMessage()); + } + } + + public function test_hydrateScalar_aliasWithSameNameAsIdentifiers_thenResultsHasAllRecords() + { + $hydrateType = Doctrine_Core::HYDRATE_SCALAR; + $expectedKeys = array('u_id', 'u_aliasId'); + $expectedKeyValues = array( + 'u_id' => 99, + ); + + $this->doTestAliasWithSameNameAsIdentifiers($hydrateType, $expectedKeys, $expectedKeyValues); + } + + public function test_hydrateArrayShallow_aliasWithSameNameAsIdentifiers_thenResultsHasAllRecords() + { + $hydrateType = Doctrine_Core::HYDRATE_ARRAY_SHALLOW; + $expectedKeys = array('id', 'aliasId'); + $expectedKeyValues = array( + 'id' => 99, + ); + + $this->doTestAliasWithSameNameAsIdentifiers($hydrateType, $expectedKeys, $expectedKeyValues); + } + + public function test_hydrateArray_aliasWithSameNameAsIdentifiers_thenResultsHasAllRecords() + { + $hydrateType = Doctrine_Core::HYDRATE_ARRAY; + $expectedKeys = array('id', 'aliasId'); + $expectedKeyValues = array( + 'id' => 99, + ); + + $this->doTestAliasWithSameNameAsIdentifiers($hydrateType, $expectedKeys, $expectedKeyValues); + } +} From 3418f4fbce430c384e223ea576fc702c589a42b8 Mon Sep 17 00:00:00 2001 From: Alexandre Quercia Date: Tue, 16 Apr 2024 03:10:39 +0200 Subject: [PATCH 06/37] fixup! fix: add failing test when aliasing a column of a joined relation --- lib/Doctrine/Hydrator/Graph.php | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/lib/Doctrine/Hydrator/Graph.php b/lib/Doctrine/Hydrator/Graph.php index 15800ffe2..0c99389e7 100644 --- a/lib/Doctrine/Hydrator/Graph.php +++ b/lib/Doctrine/Hydrator/Graph.php @@ -363,17 +363,7 @@ protected function _gatherRowData(&$data, &$cache, &$id, &$nonemptyComponents) // Hydrate aggregates in to the root component as well. // So we know that all aggregate values will always be available in the root component if ($agg) { - if ($this instanceof Doctrine_Hydrator_ArrayDriver) { - if (!isset($rowData[$dqlAlias])) { - if ($cache[$key]['isRelation']) { - $rowData[$dqlAlias] = array(); - } - } - - if ($cache[$key]['isIdentifier'] && !isset($rowData[$dqlAlias][$cache[$key]['columnName']])) { - $rowData[$dqlAlias][$cache[$key]['columnName']] = $preparedValue; - } - } + $rowData = $this->beforeAddingAggregateValue($rowData, $cache[$key], $dqlAlias, $preparedValue); $rowData[$this->_rootAlias][$fieldName] = $preparedValue; @@ -392,6 +382,23 @@ protected function _gatherRowData(&$data, &$cache, &$id, &$nonemptyComponents) return $rowData; } + protected function beforeAddingAggregateValue($rowData, $cache, $dqlAlias, $value) + { + if ($this instanceof Doctrine_Hydrator_ArrayDriver) { + if (!isset($rowData[$dqlAlias])) { + if ($cache['isRelation']) { + $rowData[$dqlAlias] = array(); + } + } + + if ($cache['isIdentifier'] && !isset($rowData[$dqlAlias][$cache['columnName']])) { + $rowData[$dqlAlias][$cache['columnName']] = $value; + } + } + + return $rowData; + } + abstract public function getElementCollection($component); abstract public function registerCollection($coll); From 6d653324125e5242455f8a0054240fa385e6663d Mon Sep 17 00:00:00 2001 From: Alexandre Quercia Date: Tue, 16 Apr 2024 03:12:00 +0200 Subject: [PATCH 07/37] fixup! fix: add failing test when aliasing a column of a joined relation --- lib/Doctrine/Hydrator/ArrayDriver.php | 17 ++++++++++++++++- lib/Doctrine/Hydrator/Graph.php | 12 ------------ 2 files changed, 16 insertions(+), 13 deletions(-) diff --git a/lib/Doctrine/Hydrator/ArrayDriver.php b/lib/Doctrine/Hydrator/ArrayDriver.php index 0850ed0e2..73299e34b 100644 --- a/lib/Doctrine/Hydrator/ArrayDriver.php +++ b/lib/Doctrine/Hydrator/ArrayDriver.php @@ -89,4 +89,19 @@ public function setLastElement(&$prev, &$coll, $index, $dqlAlias, $oneToOne) } } } -} \ No newline at end of file + + protected function beforeAddingAggregateValue($rowData, $cache, $dqlAlias, $value) + { + if (!isset($rowData[$dqlAlias])) { + if ($cache['isRelation']) { + $rowData[$dqlAlias] = array(); + } + } + + if ($cache['isIdentifier'] && !isset($rowData[$dqlAlias][$cache['columnName']])) { + $rowData[$dqlAlias][$cache['columnName']] = $value; + } + + return $rowData; + } +} diff --git a/lib/Doctrine/Hydrator/Graph.php b/lib/Doctrine/Hydrator/Graph.php index 0c99389e7..80700f4b7 100644 --- a/lib/Doctrine/Hydrator/Graph.php +++ b/lib/Doctrine/Hydrator/Graph.php @@ -384,18 +384,6 @@ protected function _gatherRowData(&$data, &$cache, &$id, &$nonemptyComponents) protected function beforeAddingAggregateValue($rowData, $cache, $dqlAlias, $value) { - if ($this instanceof Doctrine_Hydrator_ArrayDriver) { - if (!isset($rowData[$dqlAlias])) { - if ($cache['isRelation']) { - $rowData[$dqlAlias] = array(); - } - } - - if ($cache['isIdentifier'] && !isset($rowData[$dqlAlias][$cache['columnName']])) { - $rowData[$dqlAlias][$cache['columnName']] = $value; - } - } - return $rowData; } From 693ca6dbee84ebe54f1f5fa40d19f12480392ab6 Mon Sep 17 00:00:00 2001 From: Alexandre Quercia Date: Tue, 16 Apr 2024 03:21:35 +0200 Subject: [PATCH 08/37] fixup! fix: add failing test when aliasing a column of a joined relation --- lib/Doctrine/Hydrator/ArrayDriver.php | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/lib/Doctrine/Hydrator/ArrayDriver.php b/lib/Doctrine/Hydrator/ArrayDriver.php index 73299e34b..82c0a3599 100644 --- a/lib/Doctrine/Hydrator/ArrayDriver.php +++ b/lib/Doctrine/Hydrator/ArrayDriver.php @@ -91,6 +91,15 @@ public function setLastElement(&$prev, &$coll, $index, $dqlAlias, $oneToOne) } protected function beforeAddingAggregateValue($rowData, $cache, $dqlAlias, $value) + { + $rowData = $this->addSelectedRelationToRowData($rowData, $dqlAlias, $cache); + + $rowData = $this->addIdentifierColumnToRowData($rowData, $cache, $dqlAlias, $value); + + return $rowData; + } + + private function addSelectedRelationToRowData($rowData, $dqlAlias, $cache) { if (!isset($rowData[$dqlAlias])) { if ($cache['isRelation']) { @@ -98,6 +107,11 @@ protected function beforeAddingAggregateValue($rowData, $cache, $dqlAlias, $valu } } + return $rowData; + } + + private function addIdentifierColumnToRowData($cache, $rowData, $dqlAlias, $value) + { if ($cache['isIdentifier'] && !isset($rowData[$dqlAlias][$cache['columnName']])) { $rowData[$dqlAlias][$cache['columnName']] = $value; } From c61998fe92b5c4ae4c968831c9062b18914bf701 Mon Sep 17 00:00:00 2001 From: Alexandre Quercia Date: Tue, 16 Apr 2024 03:25:16 +0200 Subject: [PATCH 09/37] fixup! fix: add failing test when aliasing a column of a joined relation --- lib/Doctrine/Hydrator/ArrayDriver.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Doctrine/Hydrator/ArrayDriver.php b/lib/Doctrine/Hydrator/ArrayDriver.php index 82c0a3599..91912cfc2 100644 --- a/lib/Doctrine/Hydrator/ArrayDriver.php +++ b/lib/Doctrine/Hydrator/ArrayDriver.php @@ -110,7 +110,7 @@ private function addSelectedRelationToRowData($rowData, $dqlAlias, $cache) return $rowData; } - private function addIdentifierColumnToRowData($cache, $rowData, $dqlAlias, $value) + private function addIdentifierColumnToRowData($rowData, $cache, $dqlAlias, $value) { if ($cache['isIdentifier'] && !isset($rowData[$dqlAlias][$cache['columnName']])) { $rowData[$dqlAlias][$cache['columnName']] = $value; From 8aef340dd1405fc74da3f918e77263004aa107f6 Mon Sep 17 00:00:00 2001 From: Alexandre Quercia Date: Tue, 16 Apr 2024 08:21:44 +0200 Subject: [PATCH 10/37] fixup! fix: add failing test when aliasing a column of a joined relation --- tests/Ticket/GH134TestCase.php | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/tests/Ticket/GH134TestCase.php b/tests/Ticket/GH134TestCase.php index aba646514..ac98bdef0 100644 --- a/tests/Ticket/GH134TestCase.php +++ b/tests/Ticket/GH134TestCase.php @@ -2,7 +2,7 @@ class Doctrine_Ticket_GH134_TestCase extends Doctrine_UnitTestCase { - private function doTestWithAllColumnsAliased($hydrateType, $expectedKeys) + private function doTestWithAllColumnsAliased($hydrateType, $expectedKeys, $expectedRelationKeys = null) { try { $query = Doctrine_Query::create() @@ -17,6 +17,11 @@ private function doTestWithAllColumnsAliased($hydrateType, $expectedKeys) $this->assertEqual($expectedSql, $query->getSqlQuery()); $this->assertEqual($expectedKeys, array_keys($results[0])); + if (null !== $expectedRelationKeys) { + foreach ($expectedRelationKeys as $relationName => $relationKeys) { + $this->assertEqual($relationKeys, array_keys($results[0][$relationName])); + } + } $this->assertEqual(count($this->users), count($results)); $this->pass(); @@ -45,7 +50,10 @@ public function test_hydrateArray_withAllColumnsAliased_thenResultsHasAllRecords { $hydrateType = Doctrine_Core::HYDRATE_ARRAY; $expectedKeys = array('id', 'aliasAddress', 'Email'); + $expectedRelationKeys = array( + 'Email' => array('id', 'aliasAddress'), + ); - $this->doTestWithAllColumnsAliased($hydrateType, $expectedKeys); + $this->doTestWithAllColumnsAliased($hydrateType, $expectedKeys, $expectedRelationKeys); } } From 3a5e0ff01d76976f67242fe7df8851e984f79a60 Mon Sep 17 00:00:00 2001 From: Alexandre Quercia Date: Tue, 16 Apr 2024 20:57:44 +0200 Subject: [PATCH 11/37] Add identifier as minimum code to make test pass --- lib/Doctrine/Hydrator/ArrayDriver.php | 4 ++++ lib/Doctrine/Hydrator/Graph.php | 2 ++ 2 files changed, 6 insertions(+) diff --git a/lib/Doctrine/Hydrator/ArrayDriver.php b/lib/Doctrine/Hydrator/ArrayDriver.php index 91912cfc2..e1fdc21a9 100644 --- a/lib/Doctrine/Hydrator/ArrayDriver.php +++ b/lib/Doctrine/Hydrator/ArrayDriver.php @@ -104,6 +104,10 @@ private function addSelectedRelationToRowData($rowData, $dqlAlias, $cache) if (!isset($rowData[$dqlAlias])) { if ($cache['isRelation']) { $rowData[$dqlAlias] = array(); + + foreach ($cache['identifiers'] as $identifierField) { + $rowData[$dqlAlias][$identifierField] = null; + } } } diff --git a/lib/Doctrine/Hydrator/Graph.php b/lib/Doctrine/Hydrator/Graph.php index 80700f4b7..f37f7ad49 100644 --- a/lib/Doctrine/Hydrator/Graph.php +++ b/lib/Doctrine/Hydrator/Graph.php @@ -312,6 +312,8 @@ protected function _gatherRowData(&$data, &$cache, &$id, &$nonemptyComponents) $fieldName = $table->getFieldName($last); $cache[$key]['fieldName'] = $fieldName; + $cache[$key]['identifiers'] = (array) $table->getIdentifier(); + $cache[$key]['isRelation'] = isset($this->_queryComponents[$cache[$key]['dqlAlias']]['relation']); if (isset($this->_queryComponents[$cache[$key]['dqlAlias']]['agg'][$fieldName])) { From aba7723a992e36f12f8169a02f7f09ca21be0e84 Mon Sep 17 00:00:00 2001 From: Alexandre Quercia Date: Tue, 16 Apr 2024 21:01:44 +0200 Subject: [PATCH 12/37] Add failing test for value of relation identifier --- tests/Ticket/GH134TestCase.php | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/tests/Ticket/GH134TestCase.php b/tests/Ticket/GH134TestCase.php index ac98bdef0..f0abfd0d2 100644 --- a/tests/Ticket/GH134TestCase.php +++ b/tests/Ticket/GH134TestCase.php @@ -56,4 +56,26 @@ public function test_hydrateArray_withAllColumnsAliased_thenResultsHasAllRecords $this->doTestWithAllColumnsAliased($hydrateType, $expectedKeys, $expectedRelationKeys); } + + public function test_hydrateArray_identifierOfRelations() + { + $query = Doctrine_Query::create() + ->select('u.id, e.address as aliasAddress') + ->from('User u') + ->innerJoin('u.Email e') + ; + + $results = $query->execute(array(), Doctrine_Core::HYDRATE_ARRAY); + + $expected = array ( + 'id' => '4', + 'aliasAddress' => 'zYne@example.com', + 'Email' => array ( + 'id' => null, + 'aliasAddress' => 'zYne@example.com', + ), + ); + + $this->assertEqual($expected, $results[0]); + } } From 2cfcab3ce1eaa17aa98c78331eeee418c31f9307 Mon Sep 17 00:00:00 2001 From: Alexandre Quercia Date: Tue, 16 Apr 2024 21:16:58 +0200 Subject: [PATCH 13/37] restore previous code with issue column added twice with custom aliases --- lib/Doctrine/Hydrator/ArrayDriver.php | 35 +----------------------- lib/Doctrine/Hydrator/Graph.php | 38 +++++---------------------- lib/Doctrine/Query.php | 20 ++++++++++---- lib/Doctrine/Query/Abstract.php | 16 ----------- 4 files changed, 23 insertions(+), 86 deletions(-) diff --git a/lib/Doctrine/Hydrator/ArrayDriver.php b/lib/Doctrine/Hydrator/ArrayDriver.php index e1fdc21a9..0850ed0e2 100644 --- a/lib/Doctrine/Hydrator/ArrayDriver.php +++ b/lib/Doctrine/Hydrator/ArrayDriver.php @@ -89,37 +89,4 @@ public function setLastElement(&$prev, &$coll, $index, $dqlAlias, $oneToOne) } } } - - protected function beforeAddingAggregateValue($rowData, $cache, $dqlAlias, $value) - { - $rowData = $this->addSelectedRelationToRowData($rowData, $dqlAlias, $cache); - - $rowData = $this->addIdentifierColumnToRowData($rowData, $cache, $dqlAlias, $value); - - return $rowData; - } - - private function addSelectedRelationToRowData($rowData, $dqlAlias, $cache) - { - if (!isset($rowData[$dqlAlias])) { - if ($cache['isRelation']) { - $rowData[$dqlAlias] = array(); - - foreach ($cache['identifiers'] as $identifierField) { - $rowData[$dqlAlias][$identifierField] = null; - } - } - } - - return $rowData; - } - - private function addIdentifierColumnToRowData($rowData, $cache, $dqlAlias, $value) - { - if ($cache['isIdentifier'] && !isset($rowData[$dqlAlias][$cache['columnName']])) { - $rowData[$dqlAlias][$cache['columnName']] = $value; - } - - return $rowData; - } -} +} \ No newline at end of file diff --git a/lib/Doctrine/Hydrator/Graph.php b/lib/Doctrine/Hydrator/Graph.php index f37f7ad49..8578f4871 100644 --- a/lib/Doctrine/Hydrator/Graph.php +++ b/lib/Doctrine/Hydrator/Graph.php @@ -311,31 +311,11 @@ protected function _gatherRowData(&$data, &$cache, &$id, &$nonemptyComponents) $table = $this->_queryComponents[$cache[$key]['dqlAlias']]['table']; $fieldName = $table->getFieldName($last); $cache[$key]['fieldName'] = $fieldName; - - $cache[$key]['identifiers'] = (array) $table->getIdentifier(); - - $cache[$key]['isRelation'] = isset($this->_queryComponents[$cache[$key]['dqlAlias']]['relation']); - - if (isset($this->_queryComponents[$cache[$key]['dqlAlias']]['agg'][$fieldName])) { - $cache[$key]['isAgg'] = true; - $cache[$key]['aliasName'] = $this->_queryComponents[$cache[$key]['dqlAlias']]['agg'][$fieldName]; - } else { - $cache[$key]['isAgg'] = false; - $cache[$key]['aliasName'] = $fieldName; - } - - if (isset($this->_queryComponents[$cache[$key]['dqlAlias']]['agg_field'][$last])) { - $cache[$key]['columnName'] = $this->_queryComponents[$cache[$key]['dqlAlias']]['agg_field'][$last]; - } else { - $cache[$key]['columnName'] = $fieldName; - } - - if ($table->isIdentifier($cache[$key]['columnName'])) { + if ($table->isIdentifier($fieldName)) { $cache[$key]['isIdentifier'] = true; } else { $cache[$key]['isIdentifier'] = false; } - $type = $table->getTypeOfColumn($last); if ($type == 'integer' || $type == 'string') { $cache[$key]['isSimpleType'] = true; @@ -348,8 +328,12 @@ protected function _gatherRowData(&$data, &$cache, &$id, &$nonemptyComponents) $map = $this->_queryComponents[$cache[$key]['dqlAlias']]; $table = $map['table']; $dqlAlias = $cache[$key]['dqlAlias']; - $fieldName = $cache[$key]['aliasName']; - $agg = $cache[$key]['isAgg']; + $fieldName = $cache[$key]['fieldName']; + $agg = false; + if (isset($this->_queryComponents[$dqlAlias]['agg'][$fieldName])) { + $fieldName = $this->_queryComponents[$dqlAlias]['agg'][$fieldName]; + $agg = true; + } if ($cache[$key]['isIdentifier']) { $id[$dqlAlias] .= '|' . $value; @@ -365,10 +349,7 @@ protected function _gatherRowData(&$data, &$cache, &$id, &$nonemptyComponents) // Hydrate aggregates in to the root component as well. // So we know that all aggregate values will always be available in the root component if ($agg) { - $rowData = $this->beforeAddingAggregateValue($rowData, $cache[$key], $dqlAlias, $preparedValue); - $rowData[$this->_rootAlias][$fieldName] = $preparedValue; - if (isset($rowData[$dqlAlias])) { $rowData[$dqlAlias][$fieldName] = $preparedValue; } @@ -384,11 +365,6 @@ protected function _gatherRowData(&$data, &$cache, &$id, &$nonemptyComponents) return $rowData; } - protected function beforeAddingAggregateValue($rowData, $cache, $dqlAlias, $value) - { - return $rowData; - } - abstract public function getElementCollection($component); abstract public function registerCollection($coll); diff --git a/lib/Doctrine/Query.php b/lib/Doctrine/Query.php index a4d0b40f8..2ae35ee15 100644 --- a/lib/Doctrine/Query.php +++ b/lib/Doctrine/Query.php @@ -480,7 +480,7 @@ public function processPendingFields($componentAlias) } $sql = array(); - foreach ($fields as $fieldName) { + foreach ($fields as $fieldAlias => $fieldName) { $columnName = $table->getColumnName($fieldName); if (($owner = $table->getColumnOwner($columnName)) !== null && $owner !== $table->getComponentName()) { @@ -492,10 +492,17 @@ public function processPendingFields($componentAlias) . ' AS ' . $this->_conn->quoteIdentifier($tableAlias . '__' . $columnName); } else { - $columnName = $table->getColumnName($fieldName); + // Fix for http://www.doctrine-project.org/jira/browse/DC-585 + // Take the field alias if available + if (isset($this->_aggregateAliasMap[$fieldAlias])) { + $aliasSql = $this->_aggregateAliasMap[$fieldAlias]; + } else { + $columnName = $table->getColumnName($fieldName); + $aliasSql = $this->_conn->quoteIdentifier($tableAlias . '__' . $columnName); + } $sql[] = $this->_conn->quoteIdentifier($tableAlias) . '.' . $this->_conn->quoteIdentifier($columnName) . ' AS ' - . $this->_conn->quoteIdentifier($tableAlias . '__' . $columnName); + . $aliasSql; } } @@ -648,11 +655,14 @@ public function parseSelect($dql) $this->_queryComponents[$componentAlias]['agg'][$index] = $alias; + $this->_neededTables[] = $tableAlias; + + // Fix for http://www.doctrine-project.org/jira/browse/DC-585 + // Add selected columns to pending fields if (preg_match('/^([^\(]+)\.(\'?)(.*?)(\'?)$/', $expression, $field)) { - $this->_queryComponents[$componentAlias]['agg_field'][$index] = $field[3]; + $this->_pendingFields[$componentAlias][$alias] = $field[3]; } - $this->_neededTables[] = $tableAlias; } else { $e = explode('.', $terms[0]); diff --git a/lib/Doctrine/Query/Abstract.php b/lib/Doctrine/Query/Abstract.php index 247886f1c..6042ed18e 100644 --- a/lib/Doctrine/Query/Abstract.php +++ b/lib/Doctrine/Query/Abstract.php @@ -206,16 +206,6 @@ abstract class Doctrine_Query_Abstract * * map the name of the column / aggregate value this * component is mapped to a collection - * - * agg_field the field names for each aggregates - * Example: - * DQL: COMPONENT.FIELD as ALIAS - * SQL: TABLE.COLUMN as TABLE__0 - * $_queryComponents - * agg: - * 0: ALIAS - * agg_field: - * 0: FIELD */ protected $_queryComponents = array(); @@ -1269,9 +1259,6 @@ protected function _constructQueryFromCache($cached) if (isset($components['agg'])) { $queryComponents[$alias]['agg'] = $components['agg']; } - if (isset($components['agg_field'])) { - $queryComponents[$alias]['agg_field'] = $components['agg_field']; - } if (isset($components['map'])) { $queryComponents[$alias]['map'] = $components['map']; } @@ -1302,9 +1289,6 @@ public function getCachedForm($customComponent = null) if (isset($components['agg'])) { $componentInfo[$alias]['agg'] = $components['agg']; } - if (isset($components['agg_field'])) { - $componentInfo[$alias]['agg_field'] = $components['agg_field']; - } if (isset($components['map'])) { $componentInfo[$alias]['map'] = $components['map']; } From e2ab77274aae872273c9c9443ef7ba3596028f18 Mon Sep 17 00:00:00 2001 From: Alexandre Quercia Date: Tue, 16 Apr 2024 21:20:43 +0200 Subject: [PATCH 14/37] Add test to characterize previous behaviour and avoid regression --- tests/Ticket/GH134TestCase.php | 46 +++++++++++++++++++++++++++++++++- 1 file changed, 45 insertions(+), 1 deletion(-) diff --git a/tests/Ticket/GH134TestCase.php b/tests/Ticket/GH134TestCase.php index f0abfd0d2..991da1945 100644 --- a/tests/Ticket/GH134TestCase.php +++ b/tests/Ticket/GH134TestCase.php @@ -71,7 +71,51 @@ public function test_hydrateArray_identifierOfRelations() 'id' => '4', 'aliasAddress' => 'zYne@example.com', 'Email' => array ( - 'id' => null, + 'id' => 1, + 'aliasAddress' => 'zYne@example.com', + ), + ); + + $this->assertEqual($expected, $results[0]); + } + + public function test_hydrateArrayShallow_identifierOfRelations() + { + $query = Doctrine_Query::create() + ->select('u.id, e.address as aliasAddress') + ->from('User u') + ->innerJoin('u.Email e') + ; + + $results = $query->execute(array(), Doctrine_Core::HYDRATE_ARRAY_SHALLOW); + + $expected = array ( + 'id' => '4', + 'aliasAddress' => 'zYne@example.com', + 'Email' => array ( + 'id' => 1, + 'aliasAddress' => 'zYne@example.com', + ), + ); + + $this->assertEqual($expected, $results[0]); + } + + public function test_hydrateScalar_identifierOfRelations() + { + $query = Doctrine_Query::create() + ->select('u.id, e.address as aliasAddress') + ->from('User u') + ->innerJoin('u.Email e') + ; + + $results = $query->execute(array(), Doctrine_Core::HYDRATE_SCALAR); + + $expected = array ( + 'id' => '4', + 'aliasAddress' => 'zYne@example.com', + 'Email' => array ( + 'id' => 1, 'aliasAddress' => 'zYne@example.com', ), ); From b65e2e641d5d14d4d307c3af2b9cbbb9927c51a2 Mon Sep 17 00:00:00 2001 From: Alexandre Quercia Date: Tue, 16 Apr 2024 21:23:38 +0200 Subject: [PATCH 15/37] fixup! Add test to characterize previous behaviour and avoid regression --- tests/Ticket/GH134TestCase.php | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/tests/Ticket/GH134TestCase.php b/tests/Ticket/GH134TestCase.php index 991da1945..884507c2e 100644 --- a/tests/Ticket/GH134TestCase.php +++ b/tests/Ticket/GH134TestCase.php @@ -67,6 +67,10 @@ public function test_hydrateArray_identifierOfRelations() $results = $query->execute(array(), Doctrine_Core::HYDRATE_ARRAY); + $expectedSql = 'SELECT e.id AS e__id, e2.address AS e2__0 FROM entity e INNER JOIN email e2 ON e.email_id = e2.id WHERE (e.type = 0)'; + + $this->assertEqual($expectedSql, $query->getSqlQuery()); + $expected = array ( 'id' => '4', 'aliasAddress' => 'zYne@example.com', @@ -89,13 +93,13 @@ public function test_hydrateArrayShallow_identifierOfRelations() $results = $query->execute(array(), Doctrine_Core::HYDRATE_ARRAY_SHALLOW); + $expectedSql = 'SELECT e.id AS e__id, e2.address AS e2__0 FROM entity e INNER JOIN email e2 ON e.email_id = e2.id WHERE (e.type = 0)'; + + $this->assertEqual($expectedSql, $query->getSqlQuery()); + $expected = array ( 'id' => '4', 'aliasAddress' => 'zYne@example.com', - 'Email' => array ( - 'id' => 1, - 'aliasAddress' => 'zYne@example.com', - ), ); $this->assertEqual($expected, $results[0]); @@ -111,13 +115,13 @@ public function test_hydrateScalar_identifierOfRelations() $results = $query->execute(array(), Doctrine_Core::HYDRATE_SCALAR); + $expectedSql = 'SELECT e.id AS e__id, e2.address AS e2__0 FROM entity e INNER JOIN email e2 ON e.email_id = e2.id WHERE (e.type = 0)'; + + $this->assertEqual($expectedSql, $query->getSqlQuery()); + $expected = array ( - 'id' => '4', - 'aliasAddress' => 'zYne@example.com', - 'Email' => array ( - 'id' => 1, - 'aliasAddress' => 'zYne@example.com', - ), + 'u_id' => '4', + 'e_aliasAddress' => 'zYne@example.com', ); $this->assertEqual($expected, $results[0]); From 7ba863cd10b1575308fdab4530765af2bb957324 Mon Sep 17 00:00:00 2001 From: Alexandre Quercia Date: Tue, 16 Apr 2024 21:28:50 +0200 Subject: [PATCH 16/37] fixup! Add test to characterize previous behaviour and avoid regression --- tests/Ticket/GH134TestCase.php | 60 +++------------------------------- 1 file changed, 4 insertions(+), 56 deletions(-) diff --git a/tests/Ticket/GH134TestCase.php b/tests/Ticket/GH134TestCase.php index 884507c2e..3fde0ef0e 100644 --- a/tests/Ticket/GH134TestCase.php +++ b/tests/Ticket/GH134TestCase.php @@ -2,61 +2,6 @@ class Doctrine_Ticket_GH134_TestCase extends Doctrine_UnitTestCase { - private function doTestWithAllColumnsAliased($hydrateType, $expectedKeys, $expectedRelationKeys = null) - { - try { - $query = Doctrine_Query::create() - ->select('u.id, e.address as aliasAddress') - ->from('User u') - ->innerJoin('u.Email e') - ; - - $results = $query->execute(array(), $hydrateType); - - $expectedSql = 'SELECT e.id AS e__id, e2.address AS e2__0 FROM entity e INNER JOIN email e2 ON e.email_id = e2.id WHERE (e.type = 0)'; - - $this->assertEqual($expectedSql, $query->getSqlQuery()); - $this->assertEqual($expectedKeys, array_keys($results[0])); - if (null !== $expectedRelationKeys) { - foreach ($expectedRelationKeys as $relationName => $relationKeys) { - $this->assertEqual($relationKeys, array_keys($results[0][$relationName])); - } - } - $this->assertEqual(count($this->users), count($results)); - - $this->pass(); - } catch (Exception $e) { - $this->fail($e->getMessage()); - } - } - - public function test_hydrateScalar_withAllColumnsAliased_thenResultsHasAllRecords() - { - $hydrateType = Doctrine_Core::HYDRATE_SCALAR; - $expectedKeys = array('u_id', 'e_aliasAddress'); - - $this->doTestWithAllColumnsAliased($hydrateType, $expectedKeys); - } - - public function test_hydrateArrayShallow_withAllColumnsAliased_thenResultsHasAllRecords() - { - $hydrateType = Doctrine_Core::HYDRATE_ARRAY_SHALLOW; - $expectedKeys = array('id', 'aliasAddress'); - - $this->doTestWithAllColumnsAliased($hydrateType, $expectedKeys); - } - - public function test_hydrateArray_withAllColumnsAliased_thenResultsHasAllRecords() - { - $hydrateType = Doctrine_Core::HYDRATE_ARRAY; - $expectedKeys = array('id', 'aliasAddress', 'Email'); - $expectedRelationKeys = array( - 'Email' => array('id', 'aliasAddress'), - ); - - $this->doTestWithAllColumnsAliased($hydrateType, $expectedKeys, $expectedRelationKeys); - } - public function test_hydrateArray_identifierOfRelations() { $query = Doctrine_Query::create() @@ -67,7 +12,7 @@ public function test_hydrateArray_identifierOfRelations() $results = $query->execute(array(), Doctrine_Core::HYDRATE_ARRAY); - $expectedSql = 'SELECT e.id AS e__id, e2.address AS e2__0 FROM entity e INNER JOIN email e2 ON e.email_id = e2.id WHERE (e.type = 0)'; + $expectedSql = 'SELECT e.id AS e__id, e2.id AS e2__id, e2.address AS e2__0 FROM entity e INNER JOIN email e2 ON e.email_id = e2.id WHERE (e.type = 0)'; $this->assertEqual($expectedSql, $query->getSqlQuery()); @@ -81,6 +26,7 @@ public function test_hydrateArray_identifierOfRelations() ); $this->assertEqual($expected, $results[0]); + $this->assertEqual(count($this->users), count($results)); } public function test_hydrateArrayShallow_identifierOfRelations() @@ -103,6 +49,7 @@ public function test_hydrateArrayShallow_identifierOfRelations() ); $this->assertEqual($expected, $results[0]); + $this->assertEqual(count($this->users), count($results)); } public function test_hydrateScalar_identifierOfRelations() @@ -125,5 +72,6 @@ public function test_hydrateScalar_identifierOfRelations() ); $this->assertEqual($expected, $results[0]); + $this->assertEqual(count($this->users), count($results)); } } From 3cf389460af04d5009def71f2880f52fb5fe746d Mon Sep 17 00:00:00 2001 From: Alexandre Quercia Date: Tue, 16 Apr 2024 21:36:12 +0200 Subject: [PATCH 17/37] fixup! Add test to characterize previous behaviour and avoid regression --- tests/Ticket/585bTestCase.php | 48 ++++++++++------------------------- 1 file changed, 13 insertions(+), 35 deletions(-) diff --git a/tests/Ticket/585bTestCase.php b/tests/Ticket/585bTestCase.php index 255311128..56a37a7fa 100644 --- a/tests/Ticket/585bTestCase.php +++ b/tests/Ticket/585bTestCase.php @@ -21,61 +21,39 @@ class Doctrine_Ticket_585b_TestCase extends Doctrine_UnitTestCase { - private function doTestAliasWithSameNameAsIdentifiers($hydrateType, $expectedKeys, $expectedKeyValues = array()) + private function doTestAliasWithSameNameAsIdentifiers($hydrateType, $checkArrayIndex) { - try { - $query = Doctrine_Query::create() - ->select('99 as id, u.id as aliasId') - ->from('User u') - ; + $query = Doctrine_Query::create() + ->select('99 as id, u.id as aliasId') + ->from('User u') + ; - $results = $query->execute(array(), $hydrateType); + $results = $query->execute(array(), $hydrateType); - $expectedSql = 'SELECT 99 AS e__0, e.id AS e__1 FROM entity e WHERE (e.type = 0)'; - - $this->assertEqual($expectedSql, $query->getSqlQuery()); - $this->assertEqual($expectedKeys, array_keys($results[0])); - foreach ($expectedKeyValues as $key => $value) { - $this->assertEqual($value, $results[0][$key]); - } - $this->assertEqual(count($this->users), count($results)); - - $this->pass(); - } catch (Exception $e) { - $this->fail($e->getMessage()); - } + $this->assertEqual(99, $results[0][$checkArrayIndex]); } public function test_hydrateScalar_aliasWithSameNameAsIdentifiers_thenResultsHasAllRecords() { $hydrateType = Doctrine_Core::HYDRATE_SCALAR; - $expectedKeys = array('u_id', 'u_aliasId'); - $expectedKeyValues = array( - 'u_id' => 99, - ); + $checkArrayIndex = 'u_id'; - $this->doTestAliasWithSameNameAsIdentifiers($hydrateType, $expectedKeys, $expectedKeyValues); + $this->doTestAliasWithSameNameAsIdentifiers($hydrateType, $checkArrayIndex); } public function test_hydrateArrayShallow_aliasWithSameNameAsIdentifiers_thenResultsHasAllRecords() { $hydrateType = Doctrine_Core::HYDRATE_ARRAY_SHALLOW; - $expectedKeys = array('id', 'aliasId'); - $expectedKeyValues = array( - 'id' => 99, - ); + $checkArrayIndex = 'id'; - $this->doTestAliasWithSameNameAsIdentifiers($hydrateType, $expectedKeys, $expectedKeyValues); + $this->doTestAliasWithSameNameAsIdentifiers($hydrateType, $checkArrayIndex); } public function test_hydrateArray_aliasWithSameNameAsIdentifiers_thenResultsHasAllRecords() { $hydrateType = Doctrine_Core::HYDRATE_ARRAY; - $expectedKeys = array('id', 'aliasId'); - $expectedKeyValues = array( - 'id' => 99, - ); + $checkArrayIndex = 'id'; - $this->doTestAliasWithSameNameAsIdentifiers($hydrateType, $expectedKeys, $expectedKeyValues); + $this->doTestAliasWithSameNameAsIdentifiers($hydrateType, $checkArrayIndex); } } From 7f0cf6c70ccc340b645c7670d250061c266c864f Mon Sep 17 00:00:00 2001 From: Alexandre Quercia Date: Tue, 16 Apr 2024 21:40:54 +0200 Subject: [PATCH 18/37] fixup! Add test to characterize previous behaviour and avoid regression --- tests/Ticket/585bTestCase.php | 59 ---------------------------------- tests/Ticket/GH134TestCase.php | 36 +++++++++++++++++++++ 2 files changed, 36 insertions(+), 59 deletions(-) delete mode 100644 tests/Ticket/585bTestCase.php diff --git a/tests/Ticket/585bTestCase.php b/tests/Ticket/585bTestCase.php deleted file mode 100644 index 56a37a7fa..000000000 --- a/tests/Ticket/585bTestCase.php +++ /dev/null @@ -1,59 +0,0 @@ -. - */ - -class Doctrine_Ticket_585b_TestCase extends Doctrine_UnitTestCase -{ - private function doTestAliasWithSameNameAsIdentifiers($hydrateType, $checkArrayIndex) - { - $query = Doctrine_Query::create() - ->select('99 as id, u.id as aliasId') - ->from('User u') - ; - - $results = $query->execute(array(), $hydrateType); - - $this->assertEqual(99, $results[0][$checkArrayIndex]); - } - - public function test_hydrateScalar_aliasWithSameNameAsIdentifiers_thenResultsHasAllRecords() - { - $hydrateType = Doctrine_Core::HYDRATE_SCALAR; - $checkArrayIndex = 'u_id'; - - $this->doTestAliasWithSameNameAsIdentifiers($hydrateType, $checkArrayIndex); - } - - public function test_hydrateArrayShallow_aliasWithSameNameAsIdentifiers_thenResultsHasAllRecords() - { - $hydrateType = Doctrine_Core::HYDRATE_ARRAY_SHALLOW; - $checkArrayIndex = 'id'; - - $this->doTestAliasWithSameNameAsIdentifiers($hydrateType, $checkArrayIndex); - } - - public function test_hydrateArray_aliasWithSameNameAsIdentifiers_thenResultsHasAllRecords() - { - $hydrateType = Doctrine_Core::HYDRATE_ARRAY; - $checkArrayIndex = 'id'; - - $this->doTestAliasWithSameNameAsIdentifiers($hydrateType, $checkArrayIndex); - } -} diff --git a/tests/Ticket/GH134TestCase.php b/tests/Ticket/GH134TestCase.php index 3fde0ef0e..21eff8f73 100644 --- a/tests/Ticket/GH134TestCase.php +++ b/tests/Ticket/GH134TestCase.php @@ -74,4 +74,40 @@ public function test_hydrateScalar_identifierOfRelations() $this->assertEqual($expected, $results[0]); $this->assertEqual(count($this->users), count($results)); } + + private function doTestAliasWithSameNameAsIdentifiers($hydrateType, $checkArrayIndex) + { + $query = Doctrine_Query::create() + ->select('99 as id, u.id as aliasId') + ->from('User u') + ; + + $results = $query->execute(array(), $hydrateType); + + $this->assertEqual(99, $results[0][$checkArrayIndex]); + } + + public function test_hydrateScalar_aliasWithSameNameAsIdentifiers_willKeepOverwriteIdentifierByAlias() + { + $hydrateType = Doctrine_Core::HYDRATE_SCALAR; + $checkArrayIndex = 'u_id'; + + $this->doTestAliasWithSameNameAsIdentifiers($hydrateType, $checkArrayIndex); + } + + public function test_hydrateArrayShallow_aliasWithSameNameAsIdentifiers_willKeepOverwriteIdentifierByAlias() + { + $hydrateType = Doctrine_Core::HYDRATE_ARRAY_SHALLOW; + $checkArrayIndex = 'id'; + + $this->doTestAliasWithSameNameAsIdentifiers($hydrateType, $checkArrayIndex); + } + + public function test_hydrateArray_aliasWithSameNameAsIdentifiers_willKeepOverwriteIdentifierByAlias() + { + $hydrateType = Doctrine_Core::HYDRATE_ARRAY; + $checkArrayIndex = 'id'; + + $this->doTestAliasWithSameNameAsIdentifiers($hydrateType, $checkArrayIndex); + } } From 606ef02d9ee7d2c13cb72ecdaeb7722f21281a83 Mon Sep 17 00:00:00 2001 From: Alexandre Quercia Date: Tue, 16 Apr 2024 21:52:03 +0200 Subject: [PATCH 19/37] fixup! Add test to characterize previous behaviour and avoid regression --- tests/Ticket/GH134TestCase.php | 146 +++++++++++++-------------------- 1 file changed, 55 insertions(+), 91 deletions(-) diff --git a/tests/Ticket/GH134TestCase.php b/tests/Ticket/GH134TestCase.php index 21eff8f73..10c250eae 100644 --- a/tests/Ticket/GH134TestCase.php +++ b/tests/Ticket/GH134TestCase.php @@ -2,112 +2,76 @@ class Doctrine_Ticket_GH134_TestCase extends Doctrine_UnitTestCase { - public function test_hydrateArray_identifierOfRelations() + public function test_identifierAndRelation() { - $query = Doctrine_Query::create() - ->select('u.id, e.address as aliasAddress') - ->from('User u') - ->innerJoin('u.Email e') - ; + foreach ($this->provideIdentifierAndRelationData() as [$hydrateType, $expectedSql, $expectedFirstResult]) { + $query = Doctrine_Query::create() + ->select('u.id, e.address as aliasAddress') + ->from('User u') + ->innerJoin('u.Email e') + ; - $results = $query->execute(array(), Doctrine_Core::HYDRATE_ARRAY); + $results = $query->execute(array(), $hydrateType); - $expectedSql = 'SELECT e.id AS e__id, e2.id AS e2__id, e2.address AS e2__0 FROM entity e INNER JOIN email e2 ON e.email_id = e2.id WHERE (e.type = 0)'; + $this->assertEqual($expectedSql, $query->getSqlQuery()); - $this->assertEqual($expectedSql, $query->getSqlQuery()); - - $expected = array ( - 'id' => '4', - 'aliasAddress' => 'zYne@example.com', - 'Email' => array ( - 'id' => 1, - 'aliasAddress' => 'zYne@example.com', - ), - ); - - $this->assertEqual($expected, $results[0]); - $this->assertEqual(count($this->users), count($results)); - } - - public function test_hydrateArrayShallow_identifierOfRelations() - { - $query = Doctrine_Query::create() - ->select('u.id, e.address as aliasAddress') - ->from('User u') - ->innerJoin('u.Email e') - ; - - $results = $query->execute(array(), Doctrine_Core::HYDRATE_ARRAY_SHALLOW); - - $expectedSql = 'SELECT e.id AS e__id, e2.address AS e2__0 FROM entity e INNER JOIN email e2 ON e.email_id = e2.id WHERE (e.type = 0)'; - - $this->assertEqual($expectedSql, $query->getSqlQuery()); - - $expected = array ( - 'id' => '4', - 'aliasAddress' => 'zYne@example.com', - ); - - $this->assertEqual($expected, $results[0]); - $this->assertEqual(count($this->users), count($results)); - } - - public function test_hydrateScalar_identifierOfRelations() - { - $query = Doctrine_Query::create() - ->select('u.id, e.address as aliasAddress') - ->from('User u') - ->innerJoin('u.Email e') - ; - - $results = $query->execute(array(), Doctrine_Core::HYDRATE_SCALAR); - - $expectedSql = 'SELECT e.id AS e__id, e2.address AS e2__0 FROM entity e INNER JOIN email e2 ON e.email_id = e2.id WHERE (e.type = 0)'; - - $this->assertEqual($expectedSql, $query->getSqlQuery()); - - $expected = array ( - 'u_id' => '4', - 'e_aliasAddress' => 'zYne@example.com', - ); - - $this->assertEqual($expected, $results[0]); - $this->assertEqual(count($this->users), count($results)); + $this->assertEqual($expectedFirstResult, $results[0]); + $this->assertEqual(count($this->users), count($results)); + } } - private function doTestAliasWithSameNameAsIdentifiers($hydrateType, $checkArrayIndex) + private function provideIdentifierAndRelationData() { - $query = Doctrine_Query::create() - ->select('99 as id, u.id as aliasId') - ->from('User u') - ; - - $results = $query->execute(array(), $hydrateType); + yield [ + Doctrine_Core::HYDRATE_ARRAY, + 'SELECT e.id AS e__id, e2.id AS e2__id, e2.address AS e2__0 FROM entity e INNER JOIN email e2 ON e.email_id = e2.id WHERE (e.type = 0)', + array ( + 'id' => '4', + 'aliasAddress' => 'zYne@example.com', + 'Email' => array ( + 'id' => 1, + 'aliasAddress' => 'zYne@example.com', + ), + ), + ]; - $this->assertEqual(99, $results[0][$checkArrayIndex]); + yield [ + Doctrine_Core::HYDRATE_ARRAY_SHALLOW, + 'SELECT e.id AS e__id, e2.address AS e2__0 FROM entity e INNER JOIN email e2 ON e.email_id = e2.id WHERE (e.type = 0)', + array ( + 'id' => '4', + 'aliasAddress' => 'zYne@example.com', + ), + ]; + + yield [ + Doctrine_Core::HYDRATE_SCALAR, + 'SELECT e.id AS e__id, e2.address AS e2__0 FROM entity e INNER JOIN email e2 ON e.email_id = e2.id WHERE (e.type = 0)', + array ( + 'u_id' => '4', + 'e_aliasAddress' => 'zYne@example.com', + ), + ]; } - public function test_hydrateScalar_aliasWithSameNameAsIdentifiers_willKeepOverwriteIdentifierByAlias() + public function test_columnAliasWithSameNameAsIdentifier_willKeepOverwriteIdentifierByAlias() { - $hydrateType = Doctrine_Core::HYDRATE_SCALAR; - $checkArrayIndex = 'u_id'; + foreach ($this->provideKeepOverwriteIdentifierWithAliasData() as [$hydrateType, $checkArrayIndex]) { + $query = Doctrine_Query::create() + ->select('99 as id, u.id as aliasId') + ->from('User u') + ; - $this->doTestAliasWithSameNameAsIdentifiers($hydrateType, $checkArrayIndex); - } + $results = $query->execute(array(), $hydrateType); - public function test_hydrateArrayShallow_aliasWithSameNameAsIdentifiers_willKeepOverwriteIdentifierByAlias() - { - $hydrateType = Doctrine_Core::HYDRATE_ARRAY_SHALLOW; - $checkArrayIndex = 'id'; - - $this->doTestAliasWithSameNameAsIdentifiers($hydrateType, $checkArrayIndex); + $this->assertEqual(99, $results[0][$checkArrayIndex]); + } } - public function test_hydrateArray_aliasWithSameNameAsIdentifiers_willKeepOverwriteIdentifierByAlias() + private function provideKeepOverwriteIdentifierWithAliasData() { - $hydrateType = Doctrine_Core::HYDRATE_ARRAY; - $checkArrayIndex = 'id'; - - $this->doTestAliasWithSameNameAsIdentifiers($hydrateType, $checkArrayIndex); + yield [Doctrine_Core::HYDRATE_SCALAR, 'u_id']; + yield [Doctrine_Core::HYDRATE_ARRAY, 'id']; + yield [Doctrine_Core::HYDRATE_ARRAY_SHALLOW, 'id']; } } From ca20e82fd9a217d38e6350b9eba246b6069b51a6 Mon Sep 17 00:00:00 2001 From: Alexandre Quercia Date: Tue, 16 Apr 2024 21:36:30 +0200 Subject: [PATCH 20/37] Revert "restore previous code with issue column added twice with custom aliases" --- lib/Doctrine/Hydrator/ArrayDriver.php | 35 +++++++++++++++++++++++- lib/Doctrine/Hydrator/Graph.php | 38 ++++++++++++++++++++++----- lib/Doctrine/Query.php | 20 ++++---------- lib/Doctrine/Query/Abstract.php | 16 +++++++++++ 4 files changed, 86 insertions(+), 23 deletions(-) diff --git a/lib/Doctrine/Hydrator/ArrayDriver.php b/lib/Doctrine/Hydrator/ArrayDriver.php index 0850ed0e2..e1fdc21a9 100644 --- a/lib/Doctrine/Hydrator/ArrayDriver.php +++ b/lib/Doctrine/Hydrator/ArrayDriver.php @@ -89,4 +89,37 @@ public function setLastElement(&$prev, &$coll, $index, $dqlAlias, $oneToOne) } } } -} \ No newline at end of file + + protected function beforeAddingAggregateValue($rowData, $cache, $dqlAlias, $value) + { + $rowData = $this->addSelectedRelationToRowData($rowData, $dqlAlias, $cache); + + $rowData = $this->addIdentifierColumnToRowData($rowData, $cache, $dqlAlias, $value); + + return $rowData; + } + + private function addSelectedRelationToRowData($rowData, $dqlAlias, $cache) + { + if (!isset($rowData[$dqlAlias])) { + if ($cache['isRelation']) { + $rowData[$dqlAlias] = array(); + + foreach ($cache['identifiers'] as $identifierField) { + $rowData[$dqlAlias][$identifierField] = null; + } + } + } + + return $rowData; + } + + private function addIdentifierColumnToRowData($rowData, $cache, $dqlAlias, $value) + { + if ($cache['isIdentifier'] && !isset($rowData[$dqlAlias][$cache['columnName']])) { + $rowData[$dqlAlias][$cache['columnName']] = $value; + } + + return $rowData; + } +} diff --git a/lib/Doctrine/Hydrator/Graph.php b/lib/Doctrine/Hydrator/Graph.php index 8578f4871..f37f7ad49 100644 --- a/lib/Doctrine/Hydrator/Graph.php +++ b/lib/Doctrine/Hydrator/Graph.php @@ -311,11 +311,31 @@ protected function _gatherRowData(&$data, &$cache, &$id, &$nonemptyComponents) $table = $this->_queryComponents[$cache[$key]['dqlAlias']]['table']; $fieldName = $table->getFieldName($last); $cache[$key]['fieldName'] = $fieldName; - if ($table->isIdentifier($fieldName)) { + + $cache[$key]['identifiers'] = (array) $table->getIdentifier(); + + $cache[$key]['isRelation'] = isset($this->_queryComponents[$cache[$key]['dqlAlias']]['relation']); + + if (isset($this->_queryComponents[$cache[$key]['dqlAlias']]['agg'][$fieldName])) { + $cache[$key]['isAgg'] = true; + $cache[$key]['aliasName'] = $this->_queryComponents[$cache[$key]['dqlAlias']]['agg'][$fieldName]; + } else { + $cache[$key]['isAgg'] = false; + $cache[$key]['aliasName'] = $fieldName; + } + + if (isset($this->_queryComponents[$cache[$key]['dqlAlias']]['agg_field'][$last])) { + $cache[$key]['columnName'] = $this->_queryComponents[$cache[$key]['dqlAlias']]['agg_field'][$last]; + } else { + $cache[$key]['columnName'] = $fieldName; + } + + if ($table->isIdentifier($cache[$key]['columnName'])) { $cache[$key]['isIdentifier'] = true; } else { $cache[$key]['isIdentifier'] = false; } + $type = $table->getTypeOfColumn($last); if ($type == 'integer' || $type == 'string') { $cache[$key]['isSimpleType'] = true; @@ -328,12 +348,8 @@ protected function _gatherRowData(&$data, &$cache, &$id, &$nonemptyComponents) $map = $this->_queryComponents[$cache[$key]['dqlAlias']]; $table = $map['table']; $dqlAlias = $cache[$key]['dqlAlias']; - $fieldName = $cache[$key]['fieldName']; - $agg = false; - if (isset($this->_queryComponents[$dqlAlias]['agg'][$fieldName])) { - $fieldName = $this->_queryComponents[$dqlAlias]['agg'][$fieldName]; - $agg = true; - } + $fieldName = $cache[$key]['aliasName']; + $agg = $cache[$key]['isAgg']; if ($cache[$key]['isIdentifier']) { $id[$dqlAlias] .= '|' . $value; @@ -349,7 +365,10 @@ protected function _gatherRowData(&$data, &$cache, &$id, &$nonemptyComponents) // Hydrate aggregates in to the root component as well. // So we know that all aggregate values will always be available in the root component if ($agg) { + $rowData = $this->beforeAddingAggregateValue($rowData, $cache[$key], $dqlAlias, $preparedValue); + $rowData[$this->_rootAlias][$fieldName] = $preparedValue; + if (isset($rowData[$dqlAlias])) { $rowData[$dqlAlias][$fieldName] = $preparedValue; } @@ -365,6 +384,11 @@ protected function _gatherRowData(&$data, &$cache, &$id, &$nonemptyComponents) return $rowData; } + protected function beforeAddingAggregateValue($rowData, $cache, $dqlAlias, $value) + { + return $rowData; + } + abstract public function getElementCollection($component); abstract public function registerCollection($coll); diff --git a/lib/Doctrine/Query.php b/lib/Doctrine/Query.php index 2ae35ee15..a4d0b40f8 100644 --- a/lib/Doctrine/Query.php +++ b/lib/Doctrine/Query.php @@ -480,7 +480,7 @@ public function processPendingFields($componentAlias) } $sql = array(); - foreach ($fields as $fieldAlias => $fieldName) { + foreach ($fields as $fieldName) { $columnName = $table->getColumnName($fieldName); if (($owner = $table->getColumnOwner($columnName)) !== null && $owner !== $table->getComponentName()) { @@ -492,17 +492,10 @@ public function processPendingFields($componentAlias) . ' AS ' . $this->_conn->quoteIdentifier($tableAlias . '__' . $columnName); } else { - // Fix for http://www.doctrine-project.org/jira/browse/DC-585 - // Take the field alias if available - if (isset($this->_aggregateAliasMap[$fieldAlias])) { - $aliasSql = $this->_aggregateAliasMap[$fieldAlias]; - } else { - $columnName = $table->getColumnName($fieldName); - $aliasSql = $this->_conn->quoteIdentifier($tableAlias . '__' . $columnName); - } + $columnName = $table->getColumnName($fieldName); $sql[] = $this->_conn->quoteIdentifier($tableAlias) . '.' . $this->_conn->quoteIdentifier($columnName) . ' AS ' - . $aliasSql; + . $this->_conn->quoteIdentifier($tableAlias . '__' . $columnName); } } @@ -655,14 +648,11 @@ public function parseSelect($dql) $this->_queryComponents[$componentAlias]['agg'][$index] = $alias; - $this->_neededTables[] = $tableAlias; - - // Fix for http://www.doctrine-project.org/jira/browse/DC-585 - // Add selected columns to pending fields if (preg_match('/^([^\(]+)\.(\'?)(.*?)(\'?)$/', $expression, $field)) { - $this->_pendingFields[$componentAlias][$alias] = $field[3]; + $this->_queryComponents[$componentAlias]['agg_field'][$index] = $field[3]; } + $this->_neededTables[] = $tableAlias; } else { $e = explode('.', $terms[0]); diff --git a/lib/Doctrine/Query/Abstract.php b/lib/Doctrine/Query/Abstract.php index 6042ed18e..247886f1c 100644 --- a/lib/Doctrine/Query/Abstract.php +++ b/lib/Doctrine/Query/Abstract.php @@ -206,6 +206,16 @@ abstract class Doctrine_Query_Abstract * * map the name of the column / aggregate value this * component is mapped to a collection + * + * agg_field the field names for each aggregates + * Example: + * DQL: COMPONENT.FIELD as ALIAS + * SQL: TABLE.COLUMN as TABLE__0 + * $_queryComponents + * agg: + * 0: ALIAS + * agg_field: + * 0: FIELD */ protected $_queryComponents = array(); @@ -1259,6 +1269,9 @@ protected function _constructQueryFromCache($cached) if (isset($components['agg'])) { $queryComponents[$alias]['agg'] = $components['agg']; } + if (isset($components['agg_field'])) { + $queryComponents[$alias]['agg_field'] = $components['agg_field']; + } if (isset($components['map'])) { $queryComponents[$alias]['map'] = $components['map']; } @@ -1289,6 +1302,9 @@ public function getCachedForm($customComponent = null) if (isset($components['agg'])) { $componentInfo[$alias]['agg'] = $components['agg']; } + if (isset($components['agg_field'])) { + $componentInfo[$alias]['agg_field'] = $components['agg_field']; + } if (isset($components['map'])) { $componentInfo[$alias]['map'] = $components['map']; } From bd0f13810a5821743015e93ffe4cd29793301ab4 Mon Sep 17 00:00:00 2001 From: Alexandre Quercia Date: Wed, 17 Apr 2024 21:35:35 +0200 Subject: [PATCH 21/37] Array hydration add identifier on select SQL of selected relation --- lib/Doctrine/Query.php | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/lib/Doctrine/Query.php b/lib/Doctrine/Query.php index a4d0b40f8..b00c6d663 100644 --- a/lib/Doctrine/Query.php +++ b/lib/Doctrine/Query.php @@ -668,6 +668,18 @@ public function parseSelect($dql) $this->_pendingFields[$componentAlias][] = $field; } } + + if (Doctrine_Core::HYDRATE_ARRAY === $this->_hydrator->getHydrationMode()) { + foreach ($this->_queryComponents as $componentAlias => $queryComponent) { + if (isset($queryComponent['relation']) && isset($queryComponent['agg'])) { + $table = $queryComponent['table']; + + foreach ((array) $table->getIdentifier() as $field) { + $this->_pendingFields[$componentAlias][] = $field; + } + } + } + } } /** From 15606f5d6fa76d70b902301e546d4f1476e584fe Mon Sep 17 00:00:00 2001 From: Alexandre Quercia Date: Wed, 17 Apr 2024 21:40:35 +0200 Subject: [PATCH 22/37] Array hydration add identifier on select SQL of selected relation --- tests/Ticket/GH134TestCase.php | 53 +++++++++++++++++++++++++++++++++- 1 file changed, 52 insertions(+), 1 deletion(-) diff --git a/tests/Ticket/GH134TestCase.php b/tests/Ticket/GH134TestCase.php index 10c250eae..8b7cbcf75 100644 --- a/tests/Ticket/GH134TestCase.php +++ b/tests/Ticket/GH134TestCase.php @@ -2,7 +2,7 @@ class Doctrine_Ticket_GH134_TestCase extends Doctrine_UnitTestCase { - public function test_identifierAndRelation() + public function test_addIdentifierForSelectedRelation_withAliases() { foreach ($this->provideIdentifierAndRelationData() as [$hydrateType, $expectedSql, $expectedFirstResult]) { $query = Doctrine_Query::create() @@ -54,6 +54,57 @@ private function provideIdentifierAndRelationData() ]; } + public function test_addIdentifierForSelectedRelation_withoutAlias() + { + foreach ($this->provideIdentifierAndRelationWithoutAliasData() as [$hydrateType, $expectedSql, $expectedFirstResult]) { + $query = Doctrine_Query::create() + ->select('u.id, e.address') + ->from('User u') + ->innerJoin('u.Email e') + ; + + $results = $query->execute(array(), $hydrateType); + + $this->assertEqual($expectedSql, $query->getSqlQuery()); + + $this->assertEqual($expectedFirstResult, $results[0]); + $this->assertEqual(count($this->users), count($results)); + } + } + + private function provideIdentifierAndRelationWithoutAliasData() + { + yield [ + Doctrine_Core::HYDRATE_ARRAY, + 'SELECT e.id AS e__id, e2.id AS e2__id, e2.address AS e2__address FROM entity e INNER JOIN email e2 ON e.email_id = e2.id WHERE (e.type = 0)', + array ( + 'id' => '4', + 'Email' => array ( + 'id' => 1, + 'address' => 'zYne@example.com', + ), + ), + ]; + + yield [ + Doctrine_Core::HYDRATE_ARRAY_SHALLOW, + 'SELECT e.id AS e__id, e2.address AS e2__address FROM entity e INNER JOIN email e2 ON e.email_id = e2.id WHERE (e.type = 0)', + array ( + 'id' => '4', + 'address' => 'zYne@example.com', + ), + ]; + + yield [ + Doctrine_Core::HYDRATE_SCALAR, + 'SELECT e.id AS e__id, e2.address AS e2__address FROM entity e INNER JOIN email e2 ON e.email_id = e2.id WHERE (e.type = 0)', + array ( + 'u_id' => '4', + 'e_address' => 'zYne@example.com', + ), + ]; + } + public function test_columnAliasWithSameNameAsIdentifier_willKeepOverwriteIdentifierByAlias() { foreach ($this->provideKeepOverwriteIdentifierWithAliasData() as [$hydrateType, $checkArrayIndex]) { From 02314ab661829237f5c3fdd1756b44235eafd933 Mon Sep 17 00:00:00 2001 From: Alexandre Quercia Date: Wed, 17 Apr 2024 21:44:58 +0200 Subject: [PATCH 23/37] Array hydration add identifier on select SQL of selected relation --- lib/Doctrine/Query.php | 5 +++++ tests/Ticket/GH134TestCase.php | 4 ++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/lib/Doctrine/Query.php b/lib/Doctrine/Query.php index b00c6d663..5390c2c5a 100644 --- a/lib/Doctrine/Query.php +++ b/lib/Doctrine/Query.php @@ -669,6 +669,11 @@ public function parseSelect($dql) } } + $this->appendRelationIdentifierOnSqlSelect(); + } + + private function appendRelationIdentifierOnSqlSelect() + { if (Doctrine_Core::HYDRATE_ARRAY === $this->_hydrator->getHydrationMode()) { foreach ($this->_queryComponents as $componentAlias => $queryComponent) { if (isset($queryComponent['relation']) && isset($queryComponent['agg'])) { diff --git a/tests/Ticket/GH134TestCase.php b/tests/Ticket/GH134TestCase.php index 8b7cbcf75..29aa72727 100644 --- a/tests/Ticket/GH134TestCase.php +++ b/tests/Ticket/GH134TestCase.php @@ -4,7 +4,7 @@ class Doctrine_Ticket_GH134_TestCase extends Doctrine_UnitTestCase { public function test_addIdentifierForSelectedRelation_withAliases() { - foreach ($this->provideIdentifierAndRelationData() as [$hydrateType, $expectedSql, $expectedFirstResult]) { + foreach ($this->provideIdentifierAndRelationWithAliasData() as [$hydrateType, $expectedSql, $expectedFirstResult]) { $query = Doctrine_Query::create() ->select('u.id, e.address as aliasAddress') ->from('User u') @@ -20,7 +20,7 @@ public function test_addIdentifierForSelectedRelation_withAliases() } } - private function provideIdentifierAndRelationData() + private function provideIdentifierAndRelationWithAliasData() { yield [ Doctrine_Core::HYDRATE_ARRAY, From fc5822454830e37400cd50e8d2ec743b80beb159 Mon Sep 17 00:00:00 2001 From: Alexandre Quercia Date: Wed, 17 Apr 2024 21:51:43 +0200 Subject: [PATCH 24/37] restore previous code with issue column added twice with custom aliases --- lib/Doctrine/Hydrator/ArrayDriver.php | 35 +----------------------- lib/Doctrine/Hydrator/Graph.php | 38 +++++---------------------- lib/Doctrine/Query.php | 37 +++++++++++--------------- lib/Doctrine/Query/Abstract.php | 16 ----------- 4 files changed, 23 insertions(+), 103 deletions(-) diff --git a/lib/Doctrine/Hydrator/ArrayDriver.php b/lib/Doctrine/Hydrator/ArrayDriver.php index e1fdc21a9..0850ed0e2 100644 --- a/lib/Doctrine/Hydrator/ArrayDriver.php +++ b/lib/Doctrine/Hydrator/ArrayDriver.php @@ -89,37 +89,4 @@ public function setLastElement(&$prev, &$coll, $index, $dqlAlias, $oneToOne) } } } - - protected function beforeAddingAggregateValue($rowData, $cache, $dqlAlias, $value) - { - $rowData = $this->addSelectedRelationToRowData($rowData, $dqlAlias, $cache); - - $rowData = $this->addIdentifierColumnToRowData($rowData, $cache, $dqlAlias, $value); - - return $rowData; - } - - private function addSelectedRelationToRowData($rowData, $dqlAlias, $cache) - { - if (!isset($rowData[$dqlAlias])) { - if ($cache['isRelation']) { - $rowData[$dqlAlias] = array(); - - foreach ($cache['identifiers'] as $identifierField) { - $rowData[$dqlAlias][$identifierField] = null; - } - } - } - - return $rowData; - } - - private function addIdentifierColumnToRowData($rowData, $cache, $dqlAlias, $value) - { - if ($cache['isIdentifier'] && !isset($rowData[$dqlAlias][$cache['columnName']])) { - $rowData[$dqlAlias][$cache['columnName']] = $value; - } - - return $rowData; - } -} +} \ No newline at end of file diff --git a/lib/Doctrine/Hydrator/Graph.php b/lib/Doctrine/Hydrator/Graph.php index f37f7ad49..8578f4871 100644 --- a/lib/Doctrine/Hydrator/Graph.php +++ b/lib/Doctrine/Hydrator/Graph.php @@ -311,31 +311,11 @@ protected function _gatherRowData(&$data, &$cache, &$id, &$nonemptyComponents) $table = $this->_queryComponents[$cache[$key]['dqlAlias']]['table']; $fieldName = $table->getFieldName($last); $cache[$key]['fieldName'] = $fieldName; - - $cache[$key]['identifiers'] = (array) $table->getIdentifier(); - - $cache[$key]['isRelation'] = isset($this->_queryComponents[$cache[$key]['dqlAlias']]['relation']); - - if (isset($this->_queryComponents[$cache[$key]['dqlAlias']]['agg'][$fieldName])) { - $cache[$key]['isAgg'] = true; - $cache[$key]['aliasName'] = $this->_queryComponents[$cache[$key]['dqlAlias']]['agg'][$fieldName]; - } else { - $cache[$key]['isAgg'] = false; - $cache[$key]['aliasName'] = $fieldName; - } - - if (isset($this->_queryComponents[$cache[$key]['dqlAlias']]['agg_field'][$last])) { - $cache[$key]['columnName'] = $this->_queryComponents[$cache[$key]['dqlAlias']]['agg_field'][$last]; - } else { - $cache[$key]['columnName'] = $fieldName; - } - - if ($table->isIdentifier($cache[$key]['columnName'])) { + if ($table->isIdentifier($fieldName)) { $cache[$key]['isIdentifier'] = true; } else { $cache[$key]['isIdentifier'] = false; } - $type = $table->getTypeOfColumn($last); if ($type == 'integer' || $type == 'string') { $cache[$key]['isSimpleType'] = true; @@ -348,8 +328,12 @@ protected function _gatherRowData(&$data, &$cache, &$id, &$nonemptyComponents) $map = $this->_queryComponents[$cache[$key]['dqlAlias']]; $table = $map['table']; $dqlAlias = $cache[$key]['dqlAlias']; - $fieldName = $cache[$key]['aliasName']; - $agg = $cache[$key]['isAgg']; + $fieldName = $cache[$key]['fieldName']; + $agg = false; + if (isset($this->_queryComponents[$dqlAlias]['agg'][$fieldName])) { + $fieldName = $this->_queryComponents[$dqlAlias]['agg'][$fieldName]; + $agg = true; + } if ($cache[$key]['isIdentifier']) { $id[$dqlAlias] .= '|' . $value; @@ -365,10 +349,7 @@ protected function _gatherRowData(&$data, &$cache, &$id, &$nonemptyComponents) // Hydrate aggregates in to the root component as well. // So we know that all aggregate values will always be available in the root component if ($agg) { - $rowData = $this->beforeAddingAggregateValue($rowData, $cache[$key], $dqlAlias, $preparedValue); - $rowData[$this->_rootAlias][$fieldName] = $preparedValue; - if (isset($rowData[$dqlAlias])) { $rowData[$dqlAlias][$fieldName] = $preparedValue; } @@ -384,11 +365,6 @@ protected function _gatherRowData(&$data, &$cache, &$id, &$nonemptyComponents) return $rowData; } - protected function beforeAddingAggregateValue($rowData, $cache, $dqlAlias, $value) - { - return $rowData; - } - abstract public function getElementCollection($component); abstract public function registerCollection($coll); diff --git a/lib/Doctrine/Query.php b/lib/Doctrine/Query.php index 5390c2c5a..2ae35ee15 100644 --- a/lib/Doctrine/Query.php +++ b/lib/Doctrine/Query.php @@ -480,7 +480,7 @@ public function processPendingFields($componentAlias) } $sql = array(); - foreach ($fields as $fieldName) { + foreach ($fields as $fieldAlias => $fieldName) { $columnName = $table->getColumnName($fieldName); if (($owner = $table->getColumnOwner($columnName)) !== null && $owner !== $table->getComponentName()) { @@ -492,10 +492,17 @@ public function processPendingFields($componentAlias) . ' AS ' . $this->_conn->quoteIdentifier($tableAlias . '__' . $columnName); } else { - $columnName = $table->getColumnName($fieldName); + // Fix for http://www.doctrine-project.org/jira/browse/DC-585 + // Take the field alias if available + if (isset($this->_aggregateAliasMap[$fieldAlias])) { + $aliasSql = $this->_aggregateAliasMap[$fieldAlias]; + } else { + $columnName = $table->getColumnName($fieldName); + $aliasSql = $this->_conn->quoteIdentifier($tableAlias . '__' . $columnName); + } $sql[] = $this->_conn->quoteIdentifier($tableAlias) . '.' . $this->_conn->quoteIdentifier($columnName) . ' AS ' - . $this->_conn->quoteIdentifier($tableAlias . '__' . $columnName); + . $aliasSql; } } @@ -648,11 +655,14 @@ public function parseSelect($dql) $this->_queryComponents[$componentAlias]['agg'][$index] = $alias; + $this->_neededTables[] = $tableAlias; + + // Fix for http://www.doctrine-project.org/jira/browse/DC-585 + // Add selected columns to pending fields if (preg_match('/^([^\(]+)\.(\'?)(.*?)(\'?)$/', $expression, $field)) { - $this->_queryComponents[$componentAlias]['agg_field'][$index] = $field[3]; + $this->_pendingFields[$componentAlias][$alias] = $field[3]; } - $this->_neededTables[] = $tableAlias; } else { $e = explode('.', $terms[0]); @@ -668,23 +678,6 @@ public function parseSelect($dql) $this->_pendingFields[$componentAlias][] = $field; } } - - $this->appendRelationIdentifierOnSqlSelect(); - } - - private function appendRelationIdentifierOnSqlSelect() - { - if (Doctrine_Core::HYDRATE_ARRAY === $this->_hydrator->getHydrationMode()) { - foreach ($this->_queryComponents as $componentAlias => $queryComponent) { - if (isset($queryComponent['relation']) && isset($queryComponent['agg'])) { - $table = $queryComponent['table']; - - foreach ((array) $table->getIdentifier() as $field) { - $this->_pendingFields[$componentAlias][] = $field; - } - } - } - } } /** diff --git a/lib/Doctrine/Query/Abstract.php b/lib/Doctrine/Query/Abstract.php index 247886f1c..6042ed18e 100644 --- a/lib/Doctrine/Query/Abstract.php +++ b/lib/Doctrine/Query/Abstract.php @@ -206,16 +206,6 @@ abstract class Doctrine_Query_Abstract * * map the name of the column / aggregate value this * component is mapped to a collection - * - * agg_field the field names for each aggregates - * Example: - * DQL: COMPONENT.FIELD as ALIAS - * SQL: TABLE.COLUMN as TABLE__0 - * $_queryComponents - * agg: - * 0: ALIAS - * agg_field: - * 0: FIELD */ protected $_queryComponents = array(); @@ -1269,9 +1259,6 @@ protected function _constructQueryFromCache($cached) if (isset($components['agg'])) { $queryComponents[$alias]['agg'] = $components['agg']; } - if (isset($components['agg_field'])) { - $queryComponents[$alias]['agg_field'] = $components['agg_field']; - } if (isset($components['map'])) { $queryComponents[$alias]['map'] = $components['map']; } @@ -1302,9 +1289,6 @@ public function getCachedForm($customComponent = null) if (isset($components['agg'])) { $componentInfo[$alias]['agg'] = $components['agg']; } - if (isset($components['agg_field'])) { - $componentInfo[$alias]['agg_field'] = $components['agg_field']; - } if (isset($components['map'])) { $componentInfo[$alias]['map'] = $components['map']; } From b0df112917ddf1872f54dd305242840f1f78ac3e Mon Sep 17 00:00:00 2001 From: Alexandre Quercia Date: Wed, 17 Apr 2024 21:55:44 +0200 Subject: [PATCH 25/37] Revert "restore previous code with issue column added twice with custom aliases" This reverts commit 4dd7c86d723946f547bdb19976a9a1b9b5dc66ff. --- lib/Doctrine/Hydrator/ArrayDriver.php | 35 +++++++++++++++++++++++- lib/Doctrine/Hydrator/Graph.php | 38 ++++++++++++++++++++++----- lib/Doctrine/Query.php | 37 +++++++++++++++----------- lib/Doctrine/Query/Abstract.php | 16 +++++++++++ 4 files changed, 103 insertions(+), 23 deletions(-) diff --git a/lib/Doctrine/Hydrator/ArrayDriver.php b/lib/Doctrine/Hydrator/ArrayDriver.php index 0850ed0e2..e1fdc21a9 100644 --- a/lib/Doctrine/Hydrator/ArrayDriver.php +++ b/lib/Doctrine/Hydrator/ArrayDriver.php @@ -89,4 +89,37 @@ public function setLastElement(&$prev, &$coll, $index, $dqlAlias, $oneToOne) } } } -} \ No newline at end of file + + protected function beforeAddingAggregateValue($rowData, $cache, $dqlAlias, $value) + { + $rowData = $this->addSelectedRelationToRowData($rowData, $dqlAlias, $cache); + + $rowData = $this->addIdentifierColumnToRowData($rowData, $cache, $dqlAlias, $value); + + return $rowData; + } + + private function addSelectedRelationToRowData($rowData, $dqlAlias, $cache) + { + if (!isset($rowData[$dqlAlias])) { + if ($cache['isRelation']) { + $rowData[$dqlAlias] = array(); + + foreach ($cache['identifiers'] as $identifierField) { + $rowData[$dqlAlias][$identifierField] = null; + } + } + } + + return $rowData; + } + + private function addIdentifierColumnToRowData($rowData, $cache, $dqlAlias, $value) + { + if ($cache['isIdentifier'] && !isset($rowData[$dqlAlias][$cache['columnName']])) { + $rowData[$dqlAlias][$cache['columnName']] = $value; + } + + return $rowData; + } +} diff --git a/lib/Doctrine/Hydrator/Graph.php b/lib/Doctrine/Hydrator/Graph.php index 8578f4871..f37f7ad49 100644 --- a/lib/Doctrine/Hydrator/Graph.php +++ b/lib/Doctrine/Hydrator/Graph.php @@ -311,11 +311,31 @@ protected function _gatherRowData(&$data, &$cache, &$id, &$nonemptyComponents) $table = $this->_queryComponents[$cache[$key]['dqlAlias']]['table']; $fieldName = $table->getFieldName($last); $cache[$key]['fieldName'] = $fieldName; - if ($table->isIdentifier($fieldName)) { + + $cache[$key]['identifiers'] = (array) $table->getIdentifier(); + + $cache[$key]['isRelation'] = isset($this->_queryComponents[$cache[$key]['dqlAlias']]['relation']); + + if (isset($this->_queryComponents[$cache[$key]['dqlAlias']]['agg'][$fieldName])) { + $cache[$key]['isAgg'] = true; + $cache[$key]['aliasName'] = $this->_queryComponents[$cache[$key]['dqlAlias']]['agg'][$fieldName]; + } else { + $cache[$key]['isAgg'] = false; + $cache[$key]['aliasName'] = $fieldName; + } + + if (isset($this->_queryComponents[$cache[$key]['dqlAlias']]['agg_field'][$last])) { + $cache[$key]['columnName'] = $this->_queryComponents[$cache[$key]['dqlAlias']]['agg_field'][$last]; + } else { + $cache[$key]['columnName'] = $fieldName; + } + + if ($table->isIdentifier($cache[$key]['columnName'])) { $cache[$key]['isIdentifier'] = true; } else { $cache[$key]['isIdentifier'] = false; } + $type = $table->getTypeOfColumn($last); if ($type == 'integer' || $type == 'string') { $cache[$key]['isSimpleType'] = true; @@ -328,12 +348,8 @@ protected function _gatherRowData(&$data, &$cache, &$id, &$nonemptyComponents) $map = $this->_queryComponents[$cache[$key]['dqlAlias']]; $table = $map['table']; $dqlAlias = $cache[$key]['dqlAlias']; - $fieldName = $cache[$key]['fieldName']; - $agg = false; - if (isset($this->_queryComponents[$dqlAlias]['agg'][$fieldName])) { - $fieldName = $this->_queryComponents[$dqlAlias]['agg'][$fieldName]; - $agg = true; - } + $fieldName = $cache[$key]['aliasName']; + $agg = $cache[$key]['isAgg']; if ($cache[$key]['isIdentifier']) { $id[$dqlAlias] .= '|' . $value; @@ -349,7 +365,10 @@ protected function _gatherRowData(&$data, &$cache, &$id, &$nonemptyComponents) // Hydrate aggregates in to the root component as well. // So we know that all aggregate values will always be available in the root component if ($agg) { + $rowData = $this->beforeAddingAggregateValue($rowData, $cache[$key], $dqlAlias, $preparedValue); + $rowData[$this->_rootAlias][$fieldName] = $preparedValue; + if (isset($rowData[$dqlAlias])) { $rowData[$dqlAlias][$fieldName] = $preparedValue; } @@ -365,6 +384,11 @@ protected function _gatherRowData(&$data, &$cache, &$id, &$nonemptyComponents) return $rowData; } + protected function beforeAddingAggregateValue($rowData, $cache, $dqlAlias, $value) + { + return $rowData; + } + abstract public function getElementCollection($component); abstract public function registerCollection($coll); diff --git a/lib/Doctrine/Query.php b/lib/Doctrine/Query.php index 2ae35ee15..5390c2c5a 100644 --- a/lib/Doctrine/Query.php +++ b/lib/Doctrine/Query.php @@ -480,7 +480,7 @@ public function processPendingFields($componentAlias) } $sql = array(); - foreach ($fields as $fieldAlias => $fieldName) { + foreach ($fields as $fieldName) { $columnName = $table->getColumnName($fieldName); if (($owner = $table->getColumnOwner($columnName)) !== null && $owner !== $table->getComponentName()) { @@ -492,17 +492,10 @@ public function processPendingFields($componentAlias) . ' AS ' . $this->_conn->quoteIdentifier($tableAlias . '__' . $columnName); } else { - // Fix for http://www.doctrine-project.org/jira/browse/DC-585 - // Take the field alias if available - if (isset($this->_aggregateAliasMap[$fieldAlias])) { - $aliasSql = $this->_aggregateAliasMap[$fieldAlias]; - } else { - $columnName = $table->getColumnName($fieldName); - $aliasSql = $this->_conn->quoteIdentifier($tableAlias . '__' . $columnName); - } + $columnName = $table->getColumnName($fieldName); $sql[] = $this->_conn->quoteIdentifier($tableAlias) . '.' . $this->_conn->quoteIdentifier($columnName) . ' AS ' - . $aliasSql; + . $this->_conn->quoteIdentifier($tableAlias . '__' . $columnName); } } @@ -655,14 +648,11 @@ public function parseSelect($dql) $this->_queryComponents[$componentAlias]['agg'][$index] = $alias; - $this->_neededTables[] = $tableAlias; - - // Fix for http://www.doctrine-project.org/jira/browse/DC-585 - // Add selected columns to pending fields if (preg_match('/^([^\(]+)\.(\'?)(.*?)(\'?)$/', $expression, $field)) { - $this->_pendingFields[$componentAlias][$alias] = $field[3]; + $this->_queryComponents[$componentAlias]['agg_field'][$index] = $field[3]; } + $this->_neededTables[] = $tableAlias; } else { $e = explode('.', $terms[0]); @@ -678,6 +668,23 @@ public function parseSelect($dql) $this->_pendingFields[$componentAlias][] = $field; } } + + $this->appendRelationIdentifierOnSqlSelect(); + } + + private function appendRelationIdentifierOnSqlSelect() + { + if (Doctrine_Core::HYDRATE_ARRAY === $this->_hydrator->getHydrationMode()) { + foreach ($this->_queryComponents as $componentAlias => $queryComponent) { + if (isset($queryComponent['relation']) && isset($queryComponent['agg'])) { + $table = $queryComponent['table']; + + foreach ((array) $table->getIdentifier() as $field) { + $this->_pendingFields[$componentAlias][] = $field; + } + } + } + } } /** diff --git a/lib/Doctrine/Query/Abstract.php b/lib/Doctrine/Query/Abstract.php index 6042ed18e..247886f1c 100644 --- a/lib/Doctrine/Query/Abstract.php +++ b/lib/Doctrine/Query/Abstract.php @@ -206,6 +206,16 @@ abstract class Doctrine_Query_Abstract * * map the name of the column / aggregate value this * component is mapped to a collection + * + * agg_field the field names for each aggregates + * Example: + * DQL: COMPONENT.FIELD as ALIAS + * SQL: TABLE.COLUMN as TABLE__0 + * $_queryComponents + * agg: + * 0: ALIAS + * agg_field: + * 0: FIELD */ protected $_queryComponents = array(); @@ -1259,6 +1269,9 @@ protected function _constructQueryFromCache($cached) if (isset($components['agg'])) { $queryComponents[$alias]['agg'] = $components['agg']; } + if (isset($components['agg_field'])) { + $queryComponents[$alias]['agg_field'] = $components['agg_field']; + } if (isset($components['map'])) { $queryComponents[$alias]['map'] = $components['map']; } @@ -1289,6 +1302,9 @@ public function getCachedForm($customComponent = null) if (isset($components['agg'])) { $componentInfo[$alias]['agg'] = $components['agg']; } + if (isset($components['agg_field'])) { + $componentInfo[$alias]['agg_field'] = $components['agg_field']; + } if (isset($components['map'])) { $componentInfo[$alias]['map'] = $components['map']; } From 5820e8d02b84a9822ad15091578b4a95a8629693 Mon Sep 17 00:00:00 2001 From: Alexandre Quercia Date: Wed, 17 Apr 2024 22:16:07 +0200 Subject: [PATCH 26/37] add select relation identifier for on demand hydration --- lib/Doctrine/Query.php | 11 +++++++++++ tests/Ticket/GH134TestCase.php | 29 ++++++++++++++++++++++++++++- 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/lib/Doctrine/Query.php b/lib/Doctrine/Query.php index 5390c2c5a..0009a6fa0 100644 --- a/lib/Doctrine/Query.php +++ b/lib/Doctrine/Query.php @@ -685,6 +685,17 @@ private function appendRelationIdentifierOnSqlSelect() } } } + if (Doctrine_Core::HYDRATE_RECORD === $this->_hydrator->getHydrationMode()) { + foreach ($this->_queryComponents as $componentAlias => $queryComponent) { + if (isset($queryComponent['relation']) && isset($queryComponent['agg'])) { + $table = $queryComponent['table']; + + foreach ((array) $table->getIdentifier() as $field) { + $this->_pendingFields[$componentAlias][] = $field; + } + } + } + } } /** diff --git a/tests/Ticket/GH134TestCase.php b/tests/Ticket/GH134TestCase.php index 29aa72727..59d9191c4 100644 --- a/tests/Ticket/GH134TestCase.php +++ b/tests/Ticket/GH134TestCase.php @@ -15,7 +15,21 @@ public function test_addIdentifierForSelectedRelation_withAliases() $this->assertEqual($expectedSql, $query->getSqlQuery()); - $this->assertEqual($expectedFirstResult, $results[0]); + $firstResult = $results[0]; + + if (Doctrine_Core::HYDRATE_RECORD === $hydrateType) { + $firstResult = $firstResult->toArray(); + + $firstResult = array_intersect_key($firstResult, $expectedFirstResult); + ksort($firstResult); + ksort($expectedFirstResult); + + $firstResult['Email'] = array_intersect_key($firstResult['Email'], $expectedFirstResult['Email']); + ksort($firstResult['Email']); + ksort($expectedFirstResult['Email']); + } + + $this->assertEqual($expectedFirstResult, $firstResult); $this->assertEqual(count($this->users), count($results)); } } @@ -52,6 +66,19 @@ private function provideIdentifierAndRelationWithAliasData() 'e_aliasAddress' => 'zYne@example.com', ), ]; + + yield [ + Doctrine_Core::HYDRATE_RECORD, + 'SELECT e.id AS e__id, e2.id AS e2__id, e2.address AS e2__0 FROM entity e INNER JOIN email e2 ON e.email_id = e2.id WHERE (e.type = 0)', + array ( + 'id' => '4', + 'aliasAddress' => 'zYne@example.com', + 'Email' => array ( + 'id' => 1, + 'aliasAddress' => 'zYne@example.com', + ), + ), + ]; } public function test_addIdentifierForSelectedRelation_withoutAlias() From f48f90ead4252c996f19a2704f83f2410b6ecf1e Mon Sep 17 00:00:00 2001 From: Alexandre Quercia Date: Wed, 17 Apr 2024 22:17:15 +0200 Subject: [PATCH 27/37] fixup! add select relation identifier for on demand hydration --- tests/Ticket/GH134TestCase.php | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/tests/Ticket/GH134TestCase.php b/tests/Ticket/GH134TestCase.php index 59d9191c4..6d60f51e7 100644 --- a/tests/Ticket/GH134TestCase.php +++ b/tests/Ticket/GH134TestCase.php @@ -24,9 +24,11 @@ public function test_addIdentifierForSelectedRelation_withAliases() ksort($firstResult); ksort($expectedFirstResult); - $firstResult['Email'] = array_intersect_key($firstResult['Email'], $expectedFirstResult['Email']); - ksort($firstResult['Email']); - ksort($expectedFirstResult['Email']); + if (isset($firstResult['Email'])) { + $firstResult['Email'] = array_intersect_key($firstResult['Email'], $expectedFirstResult['Email']); + ksort($firstResult['Email']); + ksort($expectedFirstResult['Email']); + } } $this->assertEqual($expectedFirstResult, $firstResult); From d47deb60c9497b3e768bd0125d5e2a1ff64095e8 Mon Sep 17 00:00:00 2001 From: Alexandre Quercia Date: Wed, 17 Apr 2024 22:20:11 +0200 Subject: [PATCH 28/37] fixup! add select relation identifier for on demand hydration --- lib/Doctrine/Query.php | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/lib/Doctrine/Query.php b/lib/Doctrine/Query.php index 0009a6fa0..eacee8f39 100644 --- a/lib/Doctrine/Query.php +++ b/lib/Doctrine/Query.php @@ -674,18 +674,12 @@ public function parseSelect($dql) private function appendRelationIdentifierOnSqlSelect() { - if (Doctrine_Core::HYDRATE_ARRAY === $this->_hydrator->getHydrationMode()) { - foreach ($this->_queryComponents as $componentAlias => $queryComponent) { - if (isset($queryComponent['relation']) && isset($queryComponent['agg'])) { - $table = $queryComponent['table']; + $shouldSelectRelationIdentifier = in_array($this->_hydrator->getHydrationMode(), [ + Doctrine_Core::HYDRATE_ARRAY, + Doctrine_Core::HYDRATE_RECORD, + ], true); - foreach ((array) $table->getIdentifier() as $field) { - $this->_pendingFields[$componentAlias][] = $field; - } - } - } - } - if (Doctrine_Core::HYDRATE_RECORD === $this->_hydrator->getHydrationMode()) { + if ($shouldSelectRelationIdentifier) { foreach ($this->_queryComponents as $componentAlias => $queryComponent) { if (isset($queryComponent['relation']) && isset($queryComponent['agg'])) { $table = $queryComponent['table']; From 153b81c6e533ae98ee2e577ba51833438ab1de87 Mon Sep 17 00:00:00 2001 From: Alexandre Quercia Date: Wed, 17 Apr 2024 22:27:13 +0200 Subject: [PATCH 29/37] fixup! add select relation identifier for on demand hydration --- lib/Doctrine/Query.php | 1 + tests/Ticket/GH134TestCase.php | 26 +++++++++++++++++++++++--- 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/lib/Doctrine/Query.php b/lib/Doctrine/Query.php index eacee8f39..bd5977b04 100644 --- a/lib/Doctrine/Query.php +++ b/lib/Doctrine/Query.php @@ -677,6 +677,7 @@ private function appendRelationIdentifierOnSqlSelect() $shouldSelectRelationIdentifier = in_array($this->_hydrator->getHydrationMode(), [ Doctrine_Core::HYDRATE_ARRAY, Doctrine_Core::HYDRATE_RECORD, + Doctrine_Core::HYDRATE_ON_DEMAND, ], true); if ($shouldSelectRelationIdentifier) { diff --git a/tests/Ticket/GH134TestCase.php b/tests/Ticket/GH134TestCase.php index 6d60f51e7..f6cbf0d17 100644 --- a/tests/Ticket/GH134TestCase.php +++ b/tests/Ticket/GH134TestCase.php @@ -15,9 +15,13 @@ public function test_addIdentifierForSelectedRelation_withAliases() $this->assertEqual($expectedSql, $query->getSqlQuery()); - $firstResult = $results[0]; + foreach ($results as $firstResult) { + break; + } - if (Doctrine_Core::HYDRATE_RECORD === $hydrateType) { + if (Doctrine_Core::HYDRATE_RECORD === $hydrateType + || Doctrine_Core::HYDRATE_ON_DEMAND === $hydrateType + ) { $firstResult = $firstResult->toArray(); $firstResult = array_intersect_key($firstResult, $expectedFirstResult); @@ -32,7 +36,10 @@ public function test_addIdentifierForSelectedRelation_withAliases() } $this->assertEqual($expectedFirstResult, $firstResult); - $this->assertEqual(count($this->users), count($results)); + + if (Doctrine_Core::HYDRATE_ON_DEMAND !== $hydrateType) { + $this->assertEqual(count($this->users), count($results)); + } } } @@ -81,6 +88,19 @@ private function provideIdentifierAndRelationWithAliasData() ), ), ]; + + yield [ + Doctrine_Core::HYDRATE_ON_DEMAND, + 'SELECT e.id AS e__id, e2.id AS e2__id, e2.address AS e2__0 FROM entity e INNER JOIN email e2 ON e.email_id = e2.id WHERE (e.type = 0)', + array ( + 'id' => '4', + 'aliasAddress' => 'zYne@example.com', + 'Email' => array ( + 'id' => 1, + 'aliasAddress' => 'zYne@example.com', + ), + ), + ]; } public function test_addIdentifierForSelectedRelation_withoutAlias() From 69509709545c6b2df93766ab05df59d4bf8112c0 Mon Sep 17 00:00:00 2001 From: Alexandre Quercia Date: Wed, 17 Apr 2024 22:29:52 +0200 Subject: [PATCH 30/37] fixup! add select relation identifier for on demand hydration --- lib/Doctrine/Query.php | 1 - tests/Ticket/GH134TestCase.php | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/Doctrine/Query.php b/lib/Doctrine/Query.php index bd5977b04..eacee8f39 100644 --- a/lib/Doctrine/Query.php +++ b/lib/Doctrine/Query.php @@ -677,7 +677,6 @@ private function appendRelationIdentifierOnSqlSelect() $shouldSelectRelationIdentifier = in_array($this->_hydrator->getHydrationMode(), [ Doctrine_Core::HYDRATE_ARRAY, Doctrine_Core::HYDRATE_RECORD, - Doctrine_Core::HYDRATE_ON_DEMAND, ], true); if ($shouldSelectRelationIdentifier) { diff --git a/tests/Ticket/GH134TestCase.php b/tests/Ticket/GH134TestCase.php index f6cbf0d17..20f03d7eb 100644 --- a/tests/Ticket/GH134TestCase.php +++ b/tests/Ticket/GH134TestCase.php @@ -91,7 +91,7 @@ private function provideIdentifierAndRelationWithAliasData() yield [ Doctrine_Core::HYDRATE_ON_DEMAND, - 'SELECT e.id AS e__id, e2.id AS e2__id, e2.address AS e2__0 FROM entity e INNER JOIN email e2 ON e.email_id = e2.id WHERE (e.type = 0)', + 'SELECT e.id AS e__id, e2.address AS e2__0 FROM entity e INNER JOIN email e2 ON e.email_id = e2.id WHERE (e.type = 0)', array ( 'id' => '4', 'aliasAddress' => 'zYne@example.com', From 03dd3bfc32f538ebff928ef17b28b4643f1ced25 Mon Sep 17 00:00:00 2001 From: Alexandre Quercia Date: Wed, 17 Apr 2024 22:34:08 +0200 Subject: [PATCH 31/37] fixup! add select relation identifier for on demand hydration --- lib/Doctrine/Query.php | 1 + tests/Ticket/GH134TestCase.php | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/Doctrine/Query.php b/lib/Doctrine/Query.php index eacee8f39..bd5977b04 100644 --- a/lib/Doctrine/Query.php +++ b/lib/Doctrine/Query.php @@ -677,6 +677,7 @@ private function appendRelationIdentifierOnSqlSelect() $shouldSelectRelationIdentifier = in_array($this->_hydrator->getHydrationMode(), [ Doctrine_Core::HYDRATE_ARRAY, Doctrine_Core::HYDRATE_RECORD, + Doctrine_Core::HYDRATE_ON_DEMAND, ], true); if ($shouldSelectRelationIdentifier) { diff --git a/tests/Ticket/GH134TestCase.php b/tests/Ticket/GH134TestCase.php index 20f03d7eb..f6cbf0d17 100644 --- a/tests/Ticket/GH134TestCase.php +++ b/tests/Ticket/GH134TestCase.php @@ -91,7 +91,7 @@ private function provideIdentifierAndRelationWithAliasData() yield [ Doctrine_Core::HYDRATE_ON_DEMAND, - 'SELECT e.id AS e__id, e2.address AS e2__0 FROM entity e INNER JOIN email e2 ON e.email_id = e2.id WHERE (e.type = 0)', + 'SELECT e.id AS e__id, e2.id AS e2__id, e2.address AS e2__0 FROM entity e INNER JOIN email e2 ON e.email_id = e2.id WHERE (e.type = 0)', array ( 'id' => '4', 'aliasAddress' => 'zYne@example.com', From 3291eaa483ca4c35827ecdc2b0979b3936ef6ef4 Mon Sep 17 00:00:00 2001 From: Alexandre Quercia Date: Wed, 17 Apr 2024 21:51:43 +0200 Subject: [PATCH 32/37] restore previous code with issue column added twice with custom aliases --- lib/Doctrine/Hydrator/ArrayDriver.php | 35 +--------------------- lib/Doctrine/Hydrator/Graph.php | 38 +++++------------------ lib/Doctrine/Query.php | 43 ++++++++++----------------- lib/Doctrine/Query/Abstract.php | 16 ---------- 4 files changed, 23 insertions(+), 109 deletions(-) diff --git a/lib/Doctrine/Hydrator/ArrayDriver.php b/lib/Doctrine/Hydrator/ArrayDriver.php index e1fdc21a9..0850ed0e2 100644 --- a/lib/Doctrine/Hydrator/ArrayDriver.php +++ b/lib/Doctrine/Hydrator/ArrayDriver.php @@ -89,37 +89,4 @@ public function setLastElement(&$prev, &$coll, $index, $dqlAlias, $oneToOne) } } } - - protected function beforeAddingAggregateValue($rowData, $cache, $dqlAlias, $value) - { - $rowData = $this->addSelectedRelationToRowData($rowData, $dqlAlias, $cache); - - $rowData = $this->addIdentifierColumnToRowData($rowData, $cache, $dqlAlias, $value); - - return $rowData; - } - - private function addSelectedRelationToRowData($rowData, $dqlAlias, $cache) - { - if (!isset($rowData[$dqlAlias])) { - if ($cache['isRelation']) { - $rowData[$dqlAlias] = array(); - - foreach ($cache['identifiers'] as $identifierField) { - $rowData[$dqlAlias][$identifierField] = null; - } - } - } - - return $rowData; - } - - private function addIdentifierColumnToRowData($rowData, $cache, $dqlAlias, $value) - { - if ($cache['isIdentifier'] && !isset($rowData[$dqlAlias][$cache['columnName']])) { - $rowData[$dqlAlias][$cache['columnName']] = $value; - } - - return $rowData; - } -} +} \ No newline at end of file diff --git a/lib/Doctrine/Hydrator/Graph.php b/lib/Doctrine/Hydrator/Graph.php index f37f7ad49..8578f4871 100644 --- a/lib/Doctrine/Hydrator/Graph.php +++ b/lib/Doctrine/Hydrator/Graph.php @@ -311,31 +311,11 @@ protected function _gatherRowData(&$data, &$cache, &$id, &$nonemptyComponents) $table = $this->_queryComponents[$cache[$key]['dqlAlias']]['table']; $fieldName = $table->getFieldName($last); $cache[$key]['fieldName'] = $fieldName; - - $cache[$key]['identifiers'] = (array) $table->getIdentifier(); - - $cache[$key]['isRelation'] = isset($this->_queryComponents[$cache[$key]['dqlAlias']]['relation']); - - if (isset($this->_queryComponents[$cache[$key]['dqlAlias']]['agg'][$fieldName])) { - $cache[$key]['isAgg'] = true; - $cache[$key]['aliasName'] = $this->_queryComponents[$cache[$key]['dqlAlias']]['agg'][$fieldName]; - } else { - $cache[$key]['isAgg'] = false; - $cache[$key]['aliasName'] = $fieldName; - } - - if (isset($this->_queryComponents[$cache[$key]['dqlAlias']]['agg_field'][$last])) { - $cache[$key]['columnName'] = $this->_queryComponents[$cache[$key]['dqlAlias']]['agg_field'][$last]; - } else { - $cache[$key]['columnName'] = $fieldName; - } - - if ($table->isIdentifier($cache[$key]['columnName'])) { + if ($table->isIdentifier($fieldName)) { $cache[$key]['isIdentifier'] = true; } else { $cache[$key]['isIdentifier'] = false; } - $type = $table->getTypeOfColumn($last); if ($type == 'integer' || $type == 'string') { $cache[$key]['isSimpleType'] = true; @@ -348,8 +328,12 @@ protected function _gatherRowData(&$data, &$cache, &$id, &$nonemptyComponents) $map = $this->_queryComponents[$cache[$key]['dqlAlias']]; $table = $map['table']; $dqlAlias = $cache[$key]['dqlAlias']; - $fieldName = $cache[$key]['aliasName']; - $agg = $cache[$key]['isAgg']; + $fieldName = $cache[$key]['fieldName']; + $agg = false; + if (isset($this->_queryComponents[$dqlAlias]['agg'][$fieldName])) { + $fieldName = $this->_queryComponents[$dqlAlias]['agg'][$fieldName]; + $agg = true; + } if ($cache[$key]['isIdentifier']) { $id[$dqlAlias] .= '|' . $value; @@ -365,10 +349,7 @@ protected function _gatherRowData(&$data, &$cache, &$id, &$nonemptyComponents) // Hydrate aggregates in to the root component as well. // So we know that all aggregate values will always be available in the root component if ($agg) { - $rowData = $this->beforeAddingAggregateValue($rowData, $cache[$key], $dqlAlias, $preparedValue); - $rowData[$this->_rootAlias][$fieldName] = $preparedValue; - if (isset($rowData[$dqlAlias])) { $rowData[$dqlAlias][$fieldName] = $preparedValue; } @@ -384,11 +365,6 @@ protected function _gatherRowData(&$data, &$cache, &$id, &$nonemptyComponents) return $rowData; } - protected function beforeAddingAggregateValue($rowData, $cache, $dqlAlias, $value) - { - return $rowData; - } - abstract public function getElementCollection($component); abstract public function registerCollection($coll); diff --git a/lib/Doctrine/Query.php b/lib/Doctrine/Query.php index bd5977b04..2ae35ee15 100644 --- a/lib/Doctrine/Query.php +++ b/lib/Doctrine/Query.php @@ -480,7 +480,7 @@ public function processPendingFields($componentAlias) } $sql = array(); - foreach ($fields as $fieldName) { + foreach ($fields as $fieldAlias => $fieldName) { $columnName = $table->getColumnName($fieldName); if (($owner = $table->getColumnOwner($columnName)) !== null && $owner !== $table->getComponentName()) { @@ -492,10 +492,17 @@ public function processPendingFields($componentAlias) . ' AS ' . $this->_conn->quoteIdentifier($tableAlias . '__' . $columnName); } else { - $columnName = $table->getColumnName($fieldName); + // Fix for http://www.doctrine-project.org/jira/browse/DC-585 + // Take the field alias if available + if (isset($this->_aggregateAliasMap[$fieldAlias])) { + $aliasSql = $this->_aggregateAliasMap[$fieldAlias]; + } else { + $columnName = $table->getColumnName($fieldName); + $aliasSql = $this->_conn->quoteIdentifier($tableAlias . '__' . $columnName); + } $sql[] = $this->_conn->quoteIdentifier($tableAlias) . '.' . $this->_conn->quoteIdentifier($columnName) . ' AS ' - . $this->_conn->quoteIdentifier($tableAlias . '__' . $columnName); + . $aliasSql; } } @@ -648,11 +655,14 @@ public function parseSelect($dql) $this->_queryComponents[$componentAlias]['agg'][$index] = $alias; + $this->_neededTables[] = $tableAlias; + + // Fix for http://www.doctrine-project.org/jira/browse/DC-585 + // Add selected columns to pending fields if (preg_match('/^([^\(]+)\.(\'?)(.*?)(\'?)$/', $expression, $field)) { - $this->_queryComponents[$componentAlias]['agg_field'][$index] = $field[3]; + $this->_pendingFields[$componentAlias][$alias] = $field[3]; } - $this->_neededTables[] = $tableAlias; } else { $e = explode('.', $terms[0]); @@ -668,29 +678,6 @@ public function parseSelect($dql) $this->_pendingFields[$componentAlias][] = $field; } } - - $this->appendRelationIdentifierOnSqlSelect(); - } - - private function appendRelationIdentifierOnSqlSelect() - { - $shouldSelectRelationIdentifier = in_array($this->_hydrator->getHydrationMode(), [ - Doctrine_Core::HYDRATE_ARRAY, - Doctrine_Core::HYDRATE_RECORD, - Doctrine_Core::HYDRATE_ON_DEMAND, - ], true); - - if ($shouldSelectRelationIdentifier) { - foreach ($this->_queryComponents as $componentAlias => $queryComponent) { - if (isset($queryComponent['relation']) && isset($queryComponent['agg'])) { - $table = $queryComponent['table']; - - foreach ((array) $table->getIdentifier() as $field) { - $this->_pendingFields[$componentAlias][] = $field; - } - } - } - } } /** diff --git a/lib/Doctrine/Query/Abstract.php b/lib/Doctrine/Query/Abstract.php index 247886f1c..6042ed18e 100644 --- a/lib/Doctrine/Query/Abstract.php +++ b/lib/Doctrine/Query/Abstract.php @@ -206,16 +206,6 @@ abstract class Doctrine_Query_Abstract * * map the name of the column / aggregate value this * component is mapped to a collection - * - * agg_field the field names for each aggregates - * Example: - * DQL: COMPONENT.FIELD as ALIAS - * SQL: TABLE.COLUMN as TABLE__0 - * $_queryComponents - * agg: - * 0: ALIAS - * agg_field: - * 0: FIELD */ protected $_queryComponents = array(); @@ -1269,9 +1259,6 @@ protected function _constructQueryFromCache($cached) if (isset($components['agg'])) { $queryComponents[$alias]['agg'] = $components['agg']; } - if (isset($components['agg_field'])) { - $queryComponents[$alias]['agg_field'] = $components['agg_field']; - } if (isset($components['map'])) { $queryComponents[$alias]['map'] = $components['map']; } @@ -1302,9 +1289,6 @@ public function getCachedForm($customComponent = null) if (isset($components['agg'])) { $componentInfo[$alias]['agg'] = $components['agg']; } - if (isset($components['agg_field'])) { - $componentInfo[$alias]['agg_field'] = $components['agg_field']; - } if (isset($components['map'])) { $componentInfo[$alias]['map'] = $components['map']; } From a33cdbc2d1979ca765cfb446763587ead0f04508 Mon Sep 17 00:00:00 2001 From: Alexandre Quercia Date: Wed, 17 Apr 2024 22:36:26 +0200 Subject: [PATCH 33/37] Revert "restore previous code with issue column added twice with custom aliases" This reverts commit 0ec3738bd1dfad9273ab597dcdb9f433b009ad9e. --- lib/Doctrine/Hydrator/ArrayDriver.php | 35 +++++++++++++++++++++- lib/Doctrine/Hydrator/Graph.php | 38 ++++++++++++++++++----- lib/Doctrine/Query.php | 43 +++++++++++++++++---------- lib/Doctrine/Query/Abstract.php | 16 ++++++++++ 4 files changed, 109 insertions(+), 23 deletions(-) diff --git a/lib/Doctrine/Hydrator/ArrayDriver.php b/lib/Doctrine/Hydrator/ArrayDriver.php index 0850ed0e2..e1fdc21a9 100644 --- a/lib/Doctrine/Hydrator/ArrayDriver.php +++ b/lib/Doctrine/Hydrator/ArrayDriver.php @@ -89,4 +89,37 @@ public function setLastElement(&$prev, &$coll, $index, $dqlAlias, $oneToOne) } } } -} \ No newline at end of file + + protected function beforeAddingAggregateValue($rowData, $cache, $dqlAlias, $value) + { + $rowData = $this->addSelectedRelationToRowData($rowData, $dqlAlias, $cache); + + $rowData = $this->addIdentifierColumnToRowData($rowData, $cache, $dqlAlias, $value); + + return $rowData; + } + + private function addSelectedRelationToRowData($rowData, $dqlAlias, $cache) + { + if (!isset($rowData[$dqlAlias])) { + if ($cache['isRelation']) { + $rowData[$dqlAlias] = array(); + + foreach ($cache['identifiers'] as $identifierField) { + $rowData[$dqlAlias][$identifierField] = null; + } + } + } + + return $rowData; + } + + private function addIdentifierColumnToRowData($rowData, $cache, $dqlAlias, $value) + { + if ($cache['isIdentifier'] && !isset($rowData[$dqlAlias][$cache['columnName']])) { + $rowData[$dqlAlias][$cache['columnName']] = $value; + } + + return $rowData; + } +} diff --git a/lib/Doctrine/Hydrator/Graph.php b/lib/Doctrine/Hydrator/Graph.php index 8578f4871..f37f7ad49 100644 --- a/lib/Doctrine/Hydrator/Graph.php +++ b/lib/Doctrine/Hydrator/Graph.php @@ -311,11 +311,31 @@ protected function _gatherRowData(&$data, &$cache, &$id, &$nonemptyComponents) $table = $this->_queryComponents[$cache[$key]['dqlAlias']]['table']; $fieldName = $table->getFieldName($last); $cache[$key]['fieldName'] = $fieldName; - if ($table->isIdentifier($fieldName)) { + + $cache[$key]['identifiers'] = (array) $table->getIdentifier(); + + $cache[$key]['isRelation'] = isset($this->_queryComponents[$cache[$key]['dqlAlias']]['relation']); + + if (isset($this->_queryComponents[$cache[$key]['dqlAlias']]['agg'][$fieldName])) { + $cache[$key]['isAgg'] = true; + $cache[$key]['aliasName'] = $this->_queryComponents[$cache[$key]['dqlAlias']]['agg'][$fieldName]; + } else { + $cache[$key]['isAgg'] = false; + $cache[$key]['aliasName'] = $fieldName; + } + + if (isset($this->_queryComponents[$cache[$key]['dqlAlias']]['agg_field'][$last])) { + $cache[$key]['columnName'] = $this->_queryComponents[$cache[$key]['dqlAlias']]['agg_field'][$last]; + } else { + $cache[$key]['columnName'] = $fieldName; + } + + if ($table->isIdentifier($cache[$key]['columnName'])) { $cache[$key]['isIdentifier'] = true; } else { $cache[$key]['isIdentifier'] = false; } + $type = $table->getTypeOfColumn($last); if ($type == 'integer' || $type == 'string') { $cache[$key]['isSimpleType'] = true; @@ -328,12 +348,8 @@ protected function _gatherRowData(&$data, &$cache, &$id, &$nonemptyComponents) $map = $this->_queryComponents[$cache[$key]['dqlAlias']]; $table = $map['table']; $dqlAlias = $cache[$key]['dqlAlias']; - $fieldName = $cache[$key]['fieldName']; - $agg = false; - if (isset($this->_queryComponents[$dqlAlias]['agg'][$fieldName])) { - $fieldName = $this->_queryComponents[$dqlAlias]['agg'][$fieldName]; - $agg = true; - } + $fieldName = $cache[$key]['aliasName']; + $agg = $cache[$key]['isAgg']; if ($cache[$key]['isIdentifier']) { $id[$dqlAlias] .= '|' . $value; @@ -349,7 +365,10 @@ protected function _gatherRowData(&$data, &$cache, &$id, &$nonemptyComponents) // Hydrate aggregates in to the root component as well. // So we know that all aggregate values will always be available in the root component if ($agg) { + $rowData = $this->beforeAddingAggregateValue($rowData, $cache[$key], $dqlAlias, $preparedValue); + $rowData[$this->_rootAlias][$fieldName] = $preparedValue; + if (isset($rowData[$dqlAlias])) { $rowData[$dqlAlias][$fieldName] = $preparedValue; } @@ -365,6 +384,11 @@ protected function _gatherRowData(&$data, &$cache, &$id, &$nonemptyComponents) return $rowData; } + protected function beforeAddingAggregateValue($rowData, $cache, $dqlAlias, $value) + { + return $rowData; + } + abstract public function getElementCollection($component); abstract public function registerCollection($coll); diff --git a/lib/Doctrine/Query.php b/lib/Doctrine/Query.php index 2ae35ee15..bd5977b04 100644 --- a/lib/Doctrine/Query.php +++ b/lib/Doctrine/Query.php @@ -480,7 +480,7 @@ public function processPendingFields($componentAlias) } $sql = array(); - foreach ($fields as $fieldAlias => $fieldName) { + foreach ($fields as $fieldName) { $columnName = $table->getColumnName($fieldName); if (($owner = $table->getColumnOwner($columnName)) !== null && $owner !== $table->getComponentName()) { @@ -492,17 +492,10 @@ public function processPendingFields($componentAlias) . ' AS ' . $this->_conn->quoteIdentifier($tableAlias . '__' . $columnName); } else { - // Fix for http://www.doctrine-project.org/jira/browse/DC-585 - // Take the field alias if available - if (isset($this->_aggregateAliasMap[$fieldAlias])) { - $aliasSql = $this->_aggregateAliasMap[$fieldAlias]; - } else { - $columnName = $table->getColumnName($fieldName); - $aliasSql = $this->_conn->quoteIdentifier($tableAlias . '__' . $columnName); - } + $columnName = $table->getColumnName($fieldName); $sql[] = $this->_conn->quoteIdentifier($tableAlias) . '.' . $this->_conn->quoteIdentifier($columnName) . ' AS ' - . $aliasSql; + . $this->_conn->quoteIdentifier($tableAlias . '__' . $columnName); } } @@ -655,14 +648,11 @@ public function parseSelect($dql) $this->_queryComponents[$componentAlias]['agg'][$index] = $alias; - $this->_neededTables[] = $tableAlias; - - // Fix for http://www.doctrine-project.org/jira/browse/DC-585 - // Add selected columns to pending fields if (preg_match('/^([^\(]+)\.(\'?)(.*?)(\'?)$/', $expression, $field)) { - $this->_pendingFields[$componentAlias][$alias] = $field[3]; + $this->_queryComponents[$componentAlias]['agg_field'][$index] = $field[3]; } + $this->_neededTables[] = $tableAlias; } else { $e = explode('.', $terms[0]); @@ -678,6 +668,29 @@ public function parseSelect($dql) $this->_pendingFields[$componentAlias][] = $field; } } + + $this->appendRelationIdentifierOnSqlSelect(); + } + + private function appendRelationIdentifierOnSqlSelect() + { + $shouldSelectRelationIdentifier = in_array($this->_hydrator->getHydrationMode(), [ + Doctrine_Core::HYDRATE_ARRAY, + Doctrine_Core::HYDRATE_RECORD, + Doctrine_Core::HYDRATE_ON_DEMAND, + ], true); + + if ($shouldSelectRelationIdentifier) { + foreach ($this->_queryComponents as $componentAlias => $queryComponent) { + if (isset($queryComponent['relation']) && isset($queryComponent['agg'])) { + $table = $queryComponent['table']; + + foreach ((array) $table->getIdentifier() as $field) { + $this->_pendingFields[$componentAlias][] = $field; + } + } + } + } } /** diff --git a/lib/Doctrine/Query/Abstract.php b/lib/Doctrine/Query/Abstract.php index 6042ed18e..247886f1c 100644 --- a/lib/Doctrine/Query/Abstract.php +++ b/lib/Doctrine/Query/Abstract.php @@ -206,6 +206,16 @@ abstract class Doctrine_Query_Abstract * * map the name of the column / aggregate value this * component is mapped to a collection + * + * agg_field the field names for each aggregates + * Example: + * DQL: COMPONENT.FIELD as ALIAS + * SQL: TABLE.COLUMN as TABLE__0 + * $_queryComponents + * agg: + * 0: ALIAS + * agg_field: + * 0: FIELD */ protected $_queryComponents = array(); @@ -1259,6 +1269,9 @@ protected function _constructQueryFromCache($cached) if (isset($components['agg'])) { $queryComponents[$alias]['agg'] = $components['agg']; } + if (isset($components['agg_field'])) { + $queryComponents[$alias]['agg_field'] = $components['agg_field']; + } if (isset($components['map'])) { $queryComponents[$alias]['map'] = $components['map']; } @@ -1289,6 +1302,9 @@ public function getCachedForm($customComponent = null) if (isset($components['agg'])) { $componentInfo[$alias]['agg'] = $components['agg']; } + if (isset($components['agg_field'])) { + $componentInfo[$alias]['agg_field'] = $components['agg_field']; + } if (isset($components['map'])) { $componentInfo[$alias]['map'] = $components['map']; } From 51567201accd72ffb37b8d221fffd4f5715a402e Mon Sep 17 00:00:00 2001 From: Alexandre Quercia Date: Wed, 17 Apr 2024 23:02:03 +0200 Subject: [PATCH 34/37] only add relation identifier when at least one relation column is selected --- lib/Doctrine/Query.php | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/lib/Doctrine/Query.php b/lib/Doctrine/Query.php index bd5977b04..f4095b1df 100644 --- a/lib/Doctrine/Query.php +++ b/lib/Doctrine/Query.php @@ -647,6 +647,8 @@ public function parseSelect($dql) $this->_expressionMap[$alias][0] = $expression; $this->_queryComponents[$componentAlias]['agg'][$index] = $alias; + $this->_queryComponents[$componentAlias]['has_selected_column'] ??= false; + $this->_queryComponents[$componentAlias]['has_selected_column'] |= $pos === false; if (preg_match('/^([^\(]+)\.(\'?)(.*?)(\'?)$/', $expression, $field)) { $this->_queryComponents[$componentAlias]['agg_field'][$index] = $field[3]; @@ -682,7 +684,11 @@ private function appendRelationIdentifierOnSqlSelect() if ($shouldSelectRelationIdentifier) { foreach ($this->_queryComponents as $componentAlias => $queryComponent) { - if (isset($queryComponent['relation']) && isset($queryComponent['agg'])) { + if ( + isset($queryComponent['relation']) + && isset($queryComponent['agg']) + && !empty($queryComponent['has_selected_column']) + ) { $table = $queryComponent['table']; foreach ((array) $table->getIdentifier() as $field) { From 1c560245e6b94946e8c29ed5da6700455b818df1 Mon Sep 17 00:00:00 2001 From: Alexandre Quercia Date: Wed, 17 Apr 2024 23:06:51 +0200 Subject: [PATCH 35/37] fixup! only add relation identifier when at least one relation column is selected --- lib/Doctrine/Query.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/Doctrine/Query.php b/lib/Doctrine/Query.php index f4095b1df..62f238e38 100644 --- a/lib/Doctrine/Query.php +++ b/lib/Doctrine/Query.php @@ -615,6 +615,7 @@ public function parseSelect($dql) $terms = $this->_tokenizer->sqlExplode($reference, ' '); $pos = strpos($terms[0], '('); + $isColumnSelect = $pos === false; if (count($terms) > 1 || $pos !== false) { $expression = array_shift($terms); @@ -648,7 +649,7 @@ public function parseSelect($dql) $this->_queryComponents[$componentAlias]['agg'][$index] = $alias; $this->_queryComponents[$componentAlias]['has_selected_column'] ??= false; - $this->_queryComponents[$componentAlias]['has_selected_column'] |= $pos === false; + $this->_queryComponents[$componentAlias]['has_selected_column'] |= $isColumnSelect; if (preg_match('/^([^\(]+)\.(\'?)(.*?)(\'?)$/', $expression, $field)) { $this->_queryComponents[$componentAlias]['agg_field'][$index] = $field[3]; From 3777119557842eb4050334ed223e50eb8b6da74b Mon Sep 17 00:00:00 2001 From: Alexandre Quercia Date: Wed, 17 Apr 2024 23:14:14 +0200 Subject: [PATCH 36/37] add select relation identifier for array and record hierarchy --- lib/Doctrine/Query.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/Doctrine/Query.php b/lib/Doctrine/Query.php index 62f238e38..481b04965 100644 --- a/lib/Doctrine/Query.php +++ b/lib/Doctrine/Query.php @@ -679,7 +679,9 @@ private function appendRelationIdentifierOnSqlSelect() { $shouldSelectRelationIdentifier = in_array($this->_hydrator->getHydrationMode(), [ Doctrine_Core::HYDRATE_ARRAY, + Doctrine_Core::HYDRATE_ARRAY_HIERARCHY, Doctrine_Core::HYDRATE_RECORD, + Doctrine_Core::HYDRATE_RECORD_HIERARCHY, Doctrine_Core::HYDRATE_ON_DEMAND, ], true); From d7cb5dd37548ecd2d56f08746410a0fbb4340e76 Mon Sep 17 00:00:00 2001 From: Alexandre Quercia Date: Wed, 17 Apr 2024 23:39:24 +0200 Subject: [PATCH 37/37] fixup! add select relation identifier for array and record hierarchy --- lib/Doctrine/Query.php | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/lib/Doctrine/Query.php b/lib/Doctrine/Query.php index 481b04965..498da9283 100644 --- a/lib/Doctrine/Query.php +++ b/lib/Doctrine/Query.php @@ -470,11 +470,7 @@ public function processPendingFields($componentAlias) if (in_array('*', $fields)) { $fields = $table->getFieldNames(); } else { - $driverClassName = $this->_hydrator->getHydratorDriverClassName(); - // only auto-add the primary key fields if this query object is not - // a subquery of another query object or we're using a child of the Object Graph - // hydrator - if ( ! $this->_isSubquery && is_subclass_of($driverClassName, 'Doctrine_Hydrator_Graph')) { + if ($this->shouldAutoSelectIdentifiers()) { $fields = array_unique(array_merge((array) $table->getIdentifier(), $fields)); } } @@ -504,6 +500,18 @@ public function processPendingFields($componentAlias) return implode(', ', $sql); } + /* + * only auto-add the primary key fields if this query object is not + * a subquery of another query object or we're using + * a child of the Object Graph hydrator + */ + private function shouldAutoSelectIdentifiers() + { + $driverClassName = $this->_hydrator->getHydratorDriverClassName(); + + return ! $this->_isSubquery && is_subclass_of($driverClassName, 'Doctrine_Hydrator_Graph'); + } + /** * Parses a nested field * @@ -677,15 +685,7 @@ public function parseSelect($dql) private function appendRelationIdentifierOnSqlSelect() { - $shouldSelectRelationIdentifier = in_array($this->_hydrator->getHydrationMode(), [ - Doctrine_Core::HYDRATE_ARRAY, - Doctrine_Core::HYDRATE_ARRAY_HIERARCHY, - Doctrine_Core::HYDRATE_RECORD, - Doctrine_Core::HYDRATE_RECORD_HIERARCHY, - Doctrine_Core::HYDRATE_ON_DEMAND, - ], true); - - if ($shouldSelectRelationIdentifier) { + if ($this->shouldAutoSelectIdentifiers()) { foreach ($this->_queryComponents as $componentAlias => $queryComponent) { if ( isset($queryComponent['relation'])