diff --git a/Cake/ORM/Query.php b/Cake/ORM/Query.php index 453aebec633..d64e3255cc0 100644 --- a/Cake/ORM/Query.php +++ b/Cake/ORM/Query.php @@ -298,7 +298,7 @@ public function contain($associations = null, $override = false) { } $old = $this->_containments->getArrayCopy(); - $associations = array_merge($old, $this->_reformatContain($associations)); + $associations = $this->_reformatContain($associations, $old); $this->_containments->exchangeArray($associations); $this->_normalizedContainments = null; $this->_dirty(); @@ -307,22 +307,46 @@ public function contain($associations = null, $override = false) { /** * Formats the containments array so that associations are always set as keys - * in the array. + * in the array. This funciton merges the original associations array with + * the new associations provided * * @param array $associations user provided containments array + * @param array $original The original containments array to merge + * with the new one * @return array */ - protected function _reformatContain($associations) { - $result = []; + protected function _reformatContain($associations, $original) { + $result = $original; + foreach ((array)$associations as $table => $options) { + $pointer =& $result; if (is_int($table)) { $table = $options; $options = []; - } elseif (is_array($options) && !isset($this->_containOptions[$table])) { - $options = $this->_reformatContain($options); } - $result[$table] = $options; + + if (isset($this->_containOptions[$table])) { + $pointer[$table] = $options; + continue; + } + + if (strpos($table, '.')) { + $path = explode('.', $table); + $table = array_pop($path); + foreach ($path as $t) { + $pointer += [$t => []]; + $pointer =& $pointer[$t]; + } + } + + if (is_array($options)) { + $options = $this->_reformatContain($options, []); + } + + $pointer += [$table => []]; + $pointer[$table] = $options + $pointer[$table]; } + return $result; } diff --git a/Cake/Test/TestCase/ORM/QueryTest.php b/Cake/Test/TestCase/ORM/QueryTest.php index d3adb230dbf..fb736c40f0c 100644 --- a/Cake/Test/TestCase/ORM/QueryTest.php +++ b/Cake/Test/TestCase/ORM/QueryTest.php @@ -196,6 +196,53 @@ public function testContainToJoinsOneLevel() { ->contain($contains)->sql(); } +/** + * Tests setting containments using dot notation, additionaly proves that options + * are not overwritten whn combining dot notation and array notation + * + * @return void + */ + public function testContainDotNotation() { + $contains = [ + 'clients' => [ + 'orders' => [ + 'orderTypes', + 'stuff' => ['stuffTypes'] + ], + 'companies' => [ + 'foreignKey' => 'organization_id', + 'categories' + ] + ] + ]; + $query = $this->getMock('\Cake\ORM\Query', ['join'], [$this->connection, $this->table]); + $query->contain([ + 'clients.orders.stuff', + 'clients.companies.categories' => ['conditions' => ['a >' => 1]] + ]); + $expected = new \ArrayObject([ + 'clients' => [ + 'orders' => [ + 'stuff' => [] + ], + 'companies' => [ + 'categories' => [ + 'conditions' => ['a >' => 1] + ] + ] + ] + ]); + $this->assertEquals($expected, $query->contain()); + $query->contain([ + 'clients.orders' => ['fields' => ['a', 'b']], + 'clients' => ['sort' => ['a' => 'desc']], + ]); + + $expected['clients']['orders'] += ['fields' => ['a', 'b']]; + $expected['clients'] += ['sort' => ['a' => 'desc']]; + $this->assertEquals($expected, $query->contain()); + } + /** * Test that fields for contained models are aliased and added to the select clause *