diff --git a/src/ORM/Association/SelectableAssociationTrait.php b/src/ORM/Association/SelectableAssociationTrait.php index 6fbf175d39b..22a2ff9fca9 100644 --- a/src/ORM/Association/SelectableAssociationTrait.php +++ b/src/ORM/Association/SelectableAssociationTrait.php @@ -81,10 +81,15 @@ protected function _buildQuery($options) { $key = $this->_linkField($options); $filter = $options['keys']; + if ($options['strategy'] === $this::STRATEGY_SUBQUERY) { $filter = $this->_buildSubquery($options['query']); } + if (is_array($filter) && count(current($filter)) > 1) { + $filter = $this->_extractFilteringColumns($filter); + } + $fetchQuery = $this ->find('all') ->where($options['conditions']) @@ -117,6 +122,39 @@ protected function _buildQuery($options) { return $fetchQuery; } +/** + * Returns an array containing only the values from the foreignKey for the + * target table that can be used for a matching query. The values are + * taken from another array containing all the primary key columns and + * theirs values from another query. + * + * @param array $keys the primary key values extracted from a source query + * @return array + */ + protected function _extractFilteringColumns($keys) { + $primary = (array)$this->source()->primaryKey(); + $foreignKeys = (array)$this->foreignKey(); + + if (count($primary) === count($foreignKeys)) { + return $keys; + } + + $positions = array_keys(array_intersect($primary, $foreignKeys)); + $single = count($foreignKeys) === 1; + return array_map(function($keys) use ($positions, $single) { + if ($single) { + return $keys[$positions[0]]; + } + + $result = []; + foreach ($positions as $p) { + $result[] = $keys[$p]; + } + + return $result; + }, $keys); + } + /** * Appends any conditions required to load the relevant set of records in the * target table query given a filter key and some filtering values. diff --git a/tests/TestCase/ORM/QueryTest.php b/tests/TestCase/ORM/QueryTest.php index a6bacc4ac8f..42248ccd283 100644 --- a/tests/TestCase/ORM/QueryTest.php +++ b/tests/TestCase/ORM/QueryTest.php @@ -1148,12 +1148,13 @@ public function testHydrateBelongsToMany() { /** * Tests that belongsTo relations are correctly hydrated * + * @dataProvider internalStategiesProvider * @return void */ - public function testHydrateBelongsTo() { + public function testHydrateBelongsTo($strategy) { $table = TableRegistry::get('articles'); TableRegistry::get('authors'); - $table->belongsTo('authors'); + $table->belongsTo('authors', ['strategy' => $strategy]); $query = new Query($this->connection, $table); $results = $query->select() @@ -1171,16 +1172,17 @@ public function testHydrateBelongsTo() { /** * Tests that deeply nested associations are also hydrated correctly * + * @dataProvider internalStategiesProvider * @return void */ - public function testHydrateDeep() { + public function testHydrateDeep($strategy) { $table = TableRegistry::get('authors'); $article = TableRegistry::get('articles'); $table->hasMany('articles', [ 'propertyName' => 'articles', 'sort' => ['articles.id' => 'asc'] ]); - $article->belongsTo('authors'); + $article->belongsTo('authors', ['strategy' => $strategy]); $query = new Query($this->connection, $table); $results = $query->select() @@ -1900,12 +1902,13 @@ public function testColumnsFromJoin() { * Tests that it is possible to use the same association aliases in the association * chain for contain * + * @dataProvider internalStategiesProvider * @return void */ - public function testRepeatedAssociationAliases() { + public function testRepeatedAssociationAliases($strategy) { $table = TableRegistry::get('ArticlesTags'); - $table->belongsTo('Articles'); - $table->belongsTo('Tags'); + $table->belongsTo('Articles', ['strategy' => $strategy]); + $table->belongsTo('Tags', ['strategy' => $strategy]); TableRegistry::get('Tags')->belongsToMany('Articles'); $results = $table->find()->contain(['Articles', 'Tags.Articles'])->hydrate(false)->toArray(); $this->assertNotEmpty($results[0]['tag']['articles']);