Browse Source

Fixing select strategy for BelongsTo when the source table has composite
primary key

Jose Lorenzo Rodriguez 12 years ago
parent
commit
63410a82e2

+ 38 - 0
src/ORM/Association/SelectableAssociationTrait.php

@@ -81,10 +81,15 @@ trait SelectableAssociationTrait {
 		$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'])
@@ -118,6 +123,39 @@ trait SelectableAssociationTrait {
 	}
 
 /**
+ * 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.
  *

+ 10 - 7
tests/TestCase/ORM/QueryTest.php

@@ -1148,12 +1148,13 @@ class QueryTest extends TestCase {
 /**
  * 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 @@ class QueryTest extends TestCase {
 /**
  * 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 @@ class QueryTest extends TestCase {
  * 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']);