diff --git a/lib/Cake/ORM/Association.php b/lib/Cake/ORM/Association.php index ed299300dba..1df235c11b9 100644 --- a/lib/Cake/ORM/Association.php +++ b/lib/Cake/ORM/Association.php @@ -91,6 +91,13 @@ abstract class Association { */ protected $_joinType = 'LEFT'; +/** + * The property name that should be filled with data from the target table + * in the source table record. + * + * @var string + */ + protected $_property; /** * Constructor. Subclasses can override _options function to get the original @@ -108,7 +115,8 @@ public function __construct($name, array $options = []) { 'dependent', 'sourceTable', 'targetTable', - 'joinType' + 'joinType', + 'property' ]; foreach ($defaults as $property) { if (isset($options[$property])) { @@ -122,6 +130,10 @@ public function __construct($name, array $options = []) { if (empty($this->_className)) { $this->_className = $this->_name; } + + if (empty($this->_property)) { + $this->property($name); + } } /** @@ -245,6 +257,20 @@ public function joinType($type = null) { return $this->_joinType = $type; } +/** + * Sets the property name that should be filled with data from the target table + * in the source table record. + * If no arguments are passed, currently configured type is returned. + * + * @return string + */ + function property($name = null) { + if ($name !== null) { + $this->_property = $name; + } + return $this->_property; + } + /** * Override this function to initialize any concrete association class, it will * get passed the original list of options used in the constructor diff --git a/lib/Cake/ORM/Association/HasMany.php b/lib/Cake/ORM/Association/HasMany.php index ffeff9f56d0..062b09d0e27 100644 --- a/lib/Cake/ORM/Association/HasMany.php +++ b/lib/Cake/ORM/Association/HasMany.php @@ -84,14 +84,14 @@ public function eagerLoader($results, $options = []) { $resultMap = []; $key = $target->primaryKey(); foreach ($fetchQuery->execute() as $result) { - $resultMap[$result[$alias][$key]][] = $result; + $resultMap[$result[$key]][] = $result; } $sourceKey = key($fetchQuery->aliasField( $source->primaryKey(), $source->alias() )); - $targetKey = key($fetchQuery->aliasField($alias, $source->alias())); + $targetKey = key($fetchQuery->aliasField($this->property(), $source->alias())); return function($row) use ($alias, $resultMap, $sourceKey, $targetKey) { if (isset($resultMap[$row[$sourceKey]])) { $row[$targetKey] = $resultMap[$row[$sourceKey]]; diff --git a/lib/Cake/ORM/Query.php b/lib/Cake/ORM/Query.php index d7a1bba2717..9fb5be68983 100644 --- a/lib/Cake/ORM/Query.php +++ b/lib/Cake/ORM/Query.php @@ -161,10 +161,9 @@ protected function _addContainments() { } foreach ($contain as $relation => $meta) { - $associated = $this->_table->association($relation); - if (!$associated->canBeJoined()) { + if ($meta['instance'] && !$meta['instance']->canBeJoined()) { $this->_eagerLoading = true; - $this->_loadEagerly[$relation] = [$associated, $meta]; + $this->_loadEagerly[$relation] = $meta; } } } @@ -177,12 +176,14 @@ protected function _normalizeContain(Table $parent, $alias, $options) { 'fields' => 1 ]; - $table = $parent->association($alias)->target(); + $instance = $parent->association($alias); + $table = $instance->target(); $this->_aliasMap[$alias] = $table; $extra = array_diff_key($options, $defaults); $config = [ 'associations' => [], + 'instance' => $instance, 'config' => array_diff_key($options, $extra) ]; @@ -199,7 +200,7 @@ protected function _normalizeContain(Table $parent, $alias, $options) { protected function _resolveFirstLevel($source, $associations) { $result = []; foreach ($associations as $table => $options) { - $associated = $source->association($table); + $associated = $options['instance']; if ($associated && $associated->canBeJoined()) { $result[$table] = [ 'association' => $associated, @@ -226,7 +227,7 @@ protected function _eagerLoad($statement) { $statement->rewind(); foreach ($this->_loadEagerly as $association => $meta) { - $f = $meta[0]->eagerLoader($keys[$alias], $meta[1]); + $f = $meta['instance']->eagerLoader($keys[$alias], $meta); $statement = new CallbackStatement($statement, $this->connection()->driver(), $f); } diff --git a/lib/Cake/ORM/ResultSet.php b/lib/Cake/ORM/ResultSet.php index 2910a96c8eb..7afc4845ea1 100644 --- a/lib/Cake/ORM/ResultSet.php +++ b/lib/Cake/ORM/ResultSet.php @@ -86,17 +86,25 @@ protected function _groupResult() { if (empty($this->_map[$key])) { $parts = explode('__', $key); if (count($parts) > 1) { + if ($parts[0] !== $table) { + $assoc = $this->_query->repository()->association($parts[0]); + $parts[2] = $assoc->property(); + } $this->_map[$key] = $parts; } } - if (!empty($this->_map[$key])) { - list($table, $field) = $this->_map[$key]; - $value = $this->_castValue($table, $field, $value); - } + $parts = $this->_map[$key]; + list($table, $field) = $parts; + $value = $this->_castValue($table, $field, $value); - $results[$table][$field] = $value; + if (!empty($parts[2])) { + $results[$parts[2]][$field] = $value; + } else { + $results[$field] = $value; + } } + return $results; } diff --git a/lib/Cake/Test/TestCase/ORM/QueryTest.php b/lib/Cake/Test/TestCase/ORM/QueryTest.php index 5b26e1ddaf3..6714737ff6d 100644 --- a/lib/Cake/Test/TestCase/ORM/QueryTest.php +++ b/lib/Cake/Test/TestCase/ORM/QueryTest.php @@ -283,24 +283,20 @@ public function testContainResultFetchingOneLevel() { $results = $query->repository($table)->select()->contain('author')->toArray(); $expected = [ [ - 'article' => [ - 'id' => 1, - 'title' => 'a title', - 'body' => 'a body', - 'author_id' => 1 - ], + 'id' => 1, + 'title' => 'a title', + 'body' => 'a body', + 'author_id' => 1, 'author' => [ 'id' => 1, 'name' => 'Chuck Norris' ] ], [ - 'article' => [ - 'id' => 2, - 'title' => 'another title', - 'body' => 'another body', - 'author_id' => 2 - ], + 'id' => 2, + 'title' => 'another title', + 'body' => 'another body', + 'author_id' => 2, 'author' => [ 'id' => 2, 'name' => 'Bruce Lee' @@ -321,39 +317,31 @@ public function testHasManyEagerLoading() { $query = new Query($this->connection); $table = Table::build('author', ['connection' => $this->connection]); Table::build('article', ['connection' => $this->connection]); - $table->hasMany('article'); + $table->hasMany('article', ['property' => 'articles']); $results = $query->repository($table)->select()->contain('article')->toArray(); $expected = [ [ - 'author' => [ - 'id' => 1, - 'name' => 'Chuck Norris', - 'article' => [ - [ - 'article' => [ - 'id' => 1, - 'title' => 'a title', - 'body' => 'a body', - 'author_id' => 1 - ] - ] + 'id' => 1, + 'name' => 'Chuck Norris', + 'articles' => [ + [ + 'id' => 1, + 'title' => 'a title', + 'body' => 'a body', + 'author_id' => 1 ] ] ], [ - 'author' => [ - 'id' => 2, - 'name' => 'Bruce Lee', - 'article' => [ - [ - 'article' => [ - 'id' => 2, - 'title' => 'another title', - 'body' => 'another body', - 'author_id' => 2 - ] - ] + 'id' => 2, + 'name' => 'Bruce Lee', + 'articles' => [ + [ + 'id' => 2, + 'title' => 'another title', + 'body' => 'another body', + 'author_id' => 2 ] ] ]