Browse Source

Simplifying result nesting by alwasy passing the expected nest key.

This removes the hackery introduced for nesting results from an external
query and crazy names like '___collection_'
Jose Lorenzo Rodriguez 12 years ago
parent
commit
69918ec6f0

+ 6 - 3
src/ORM/Association.php

@@ -447,15 +447,18 @@ abstract class Association {
  * source results.
  *
  * @param array $row
+ * @param string $nestKey The array key under which the results for this association
+ * should be found
  * @param boolean $joined Whether or not the row is a result of a direct join
  * with this association
  * @return array
  */
-	public function transformRow($row, $joined) {
+	public function transformRow($row, $nestKey, $joined) {
 		$sourceAlias = $this->source()->alias();
-		$targetAlias = $this->target()->alias();
+		$nestKey = $nestKey ?: $this->_name;
 		if (isset($row[$sourceAlias])) {
-			$row[$sourceAlias][$this->property()] = $row[$targetAlias];
+			$row[$sourceAlias][$this->property()] = $row[$nestKey];
+			unset($row[$nestKey]);
 		}
 		return $row;
 	}

+ 0 - 17
src/ORM/Association/BelongsTo.php

@@ -103,23 +103,6 @@ class BelongsTo extends Association {
 	}
 
 /**
- * {@inheritdoc}
- *
- */
-	public function transformRow($row, $joined) {
-		if ($this->strategy() === $this::STRATEGY_JOIN) {
-			return parent::transformRow($row, $joined);
-		}
-
-		$sourceAlias = $this->source()->alias();
-		$nestKey = $this->_nestingKey();
-		if (isset($row[$nestKey])) {
-			$row[$sourceAlias][$this->property()] = $row[$nestKey];
-		}
-		return $row;
-	}
-
-/**
  * Takes an entity from the source table and looks if there is a field
  * matching the property name for this association. The found entity will be
  * saved on the target table for this association by passing supplied

+ 4 - 9
src/ORM/Association/BelongsToMany.php

@@ -32,7 +32,6 @@ class BelongsToMany extends Association {
 	use ExternalAssociationTrait {
 		_options as _externalOptions;
 		_addFilteringCondition as _addExternalConditions;
-		transformRow as protected _transformRow;
 	}
 
 /**
@@ -236,21 +235,17 @@ class BelongsToMany extends Association {
 	}
 
 /**
- * Correctly nests a result row associated values into the correct array keys inside the
- * source results.
+ * {@inheritdoc}
  *
- * @param array $row
- * @param boolean $joined Whether or not the row is a result of a direct join
- * with this association
- * @return array
  */
-	public function transformRow($row, $joined) {
+	public function transformRow($row, $nestKey, $joined) {
 		$alias = $this->junction()->alias();
 		if ($joined) {
 			$row[$this->target()->alias()][$this->_junctionProperty] = $row[$alias];
 			unset($row[$alias]);
 		}
-		return $this->_transformRow($row, $joined);
+
+		return parent::transformRow($row, $nestKey, $joined);
 	}
 
 /**

+ 0 - 34
src/ORM/Association/ExternalAssociationTrait.php

@@ -78,30 +78,6 @@ trait ExternalAssociationTrait {
 	}
 
 /**
- * Correctly nests a result row associated values into the correct array keys inside the
- * source results.
- *
- * @param array $row
- * @param boolean $joined Whether or not the row is a result of a direct join
- * with this association
- * @return array
- */
-	public function transformRow($row, $joined) {
-		$sourceAlias = $this->source()->alias();
-		$targetAlias = $this->target()->alias();
-
-		$collectionAlias = $this->_name . '___collection_';
-		if (isset($row[$collectionAlias])) {
-			$values = $row[$collectionAlias];
-		} else {
-			$values = $row[$this->_name];
-		}
-
-		$row[$sourceAlias][$this->property()] = $values;
-		return $row;
-	}
-
-/**
  * Returns the default options to use for the eagerLoader
  *
  * @return array
@@ -131,16 +107,6 @@ trait ExternalAssociationTrait {
 	}
 
 /**
- * Returns the key under which the eagerLoader will put this association results
- *
- * @return void
- */
-	protected function _nestingKey() {
-		return $this->_name . '___collection_';
-	}
-
-
-/**
  * Parse extra options passed in the constructor.
  *
  * @param array $opts original list of options passed in constructor

+ 5 - 13
src/ORM/Association/SelectableAssociationTrait.php

@@ -50,7 +50,7 @@ trait SelectableAssociationTrait {
 			$fetchQuery = $queryBuilder($fetchQuery);
 		}
 		$resultMap = $this->_buildResultMap($fetchQuery, $options);
-		return $this->_resultInjector($fetchQuery, $resultMap);
+		return $this->_resultInjector($fetchQuery, $resultMap, $options);
 	}
 
 /**
@@ -62,7 +62,8 @@ trait SelectableAssociationTrait {
 		return [
 			'foreignKey' => $this->foreignKey(),
 			'conditions' => [],
-			'strategy' => $this->strategy()
+			'strategy' => $this->strategy(),
+			'nestKey' => $this->_name
 		];
 	}
 
@@ -199,7 +200,7 @@ trait SelectableAssociationTrait {
  * the corresponding target table results as value.
  * @return \Closure
  */
-	protected function _resultInjector($fetchQuery, $resultMap) {
+	protected function _resultInjector($fetchQuery, $resultMap, $options) {
 		$source = $this->source();
 		$sAlias = $source->alias();
 		$keys = $this->type() === $this::MANY_TO_ONE ?
@@ -211,7 +212,7 @@ trait SelectableAssociationTrait {
 			$sourceKeys[] = key($fetchQuery->aliasField($key, $sAlias));
 		}
 
-		$nestKey = $this->_nestingKey();
+		$nestKey = $options['nestKey'];
 		if (count($sourceKeys) > 1) {
 			return $this->_multiKeysInjector($resultMap, $sourceKeys, $nestKey);
 		}
@@ -226,15 +227,6 @@ trait SelectableAssociationTrait {
 	}
 
 /**
- * Returns the key under which the eagerLoader will put this association results
- *
- * @return string
- */
-	protected function _nestingKey() {
-		return $this->property();
-	}
-
-/**
  * Returns a callable to be used for each row in a query result set
  * for injecting the eager loaded rows when the matching needs to
  * be done with multiple foreign keys

+ 6 - 1
src/ORM/EagerLoader.php

@@ -389,7 +389,12 @@ class EagerLoader {
 
 			$keys = isset($collected[$alias]) ? $collected[$alias] : null;
 			$f = $meta['instance']->eagerLoader(
-				$meta['config'] + ['query' => $query, 'contain' => $contain, 'keys' => $keys]
+				$meta['config'] + [
+					'query' => $query,
+					'contain' => $contain,
+					'keys' => $keys,
+					'nestKey' => $meta['aliasPath']
+				]
 			);
 			$statement = new CallbackStatement($statement, $driver, $f);
 		}

+ 7 - 5
src/ORM/ResultSet.php

@@ -309,7 +309,8 @@ class ResultSet implements Countable, Iterator, Serializable, JsonSerializable {
 					'alias' => $assoc,
 					'instance' => $meta['instance'],
 					'canBeJoined' => $meta['canBeJoined'],
-					'entityClass' => $meta['instance']->target()->entityClass()
+					'entityClass' => $meta['instance']->target()->entityClass(),
+					'nestKey' => $meta['canBeJoined'] ? $assoc : $meta['aliasPath']
 				];
 				if (!empty($meta['associations'])) {
 					$visitor($meta['associations']);
@@ -351,7 +352,7 @@ class ResultSet implements Countable, Iterator, Serializable, JsonSerializable {
 			$table = $defaultAlias;
 			$field = $key;
 
-			if (strpos($key, '___collection_') !== false) {
+			if (isset($this->_associationMap[$key])) {
 				$results[$key] = $value;
 				continue;
 			}
@@ -383,9 +384,10 @@ class ResultSet implements Countable, Iterator, Serializable, JsonSerializable {
 			'markNew' => false,
 			'guard' => false
 		];
+			
 		foreach (array_reverse($this->_associationMap) as $assoc) {
-			$alias = $assoc['alias'];
-			if (!isset($results[$alias]) && !isset($results[$alias . '___collection_'])) {
+			$alias = $assoc['nestKey'];
+			if (!isset($results[$alias])) {
 				continue;
 			}
 
@@ -404,7 +406,7 @@ class ResultSet implements Countable, Iterator, Serializable, JsonSerializable {
 				$results[$alias] = $entity;
 			}
 
-			$results = $instance->transformRow($results, $assoc['canBeJoined']);
+			$results = $instance->transformRow($results, $alias, $assoc['canBeJoined']);
 		}
 
 		foreach ($presentAliases as $alias => $present) {

+ 9 - 3
tests/TestCase/ORM/QueryRegressionTest.php

@@ -72,10 +72,16 @@ class QueryRegressionTest extends TestCase {
 
 		$table = TableRegistry::get('Articles');
 		$table->belongsTo('Authors');
-		$table->hasOne('Things');
+		$table->hasOne('Things', ['propertyName' => 'articles_tag']);
 		
-		$table->Authors->target()->hasOne('Stuff', ['foreignKey' => 'id']);
-		$table->Things->target()->belongsTo('Stuff', ['foreignKey' => 'tag_id']);
+		$table->Authors->target()->hasOne('Stuff', [
+			'foreignKey' => 'id',
+			'propertyName' => 'favorite_tag'
+		]);
+		$table->Things->target()->belongsTo('Stuff', [
+			'foreignKey' => 'tag_id',
+			'propertyName' => 'foo']
+		);
 
 		$results = $table->find()
 			->contain(['Authors.Stuff', 'Things.Stuff'])