Browse Source

Added support for using formatResult on deep attachable associations

Jose Lorenzo Rodriguez 12 years ago
parent
commit
ec0cc6a8b3

+ 6 - 6
src/ORM/Association.php

@@ -430,7 +430,7 @@ abstract class Association {
 		$query->join([$target->alias() => array_intersect_key($options, $joinOptions)]);
 	
 		$this->_appendFields($query, $dummy, $options);
-		$this->_formatAssociationResults($query, $dummy);
+		$this->_formatAssociationResults($query, $dummy, $options);
 		$this->_bindNewAssociations($query, $dummy, $options);
 	}
 
@@ -505,15 +505,15 @@ abstract class Association {
 		}
 	}
 
-	protected function _formatAssociationResults($query, $surrogate) {
+	protected function _formatAssociationResults($query, $surrogate, $options) {
 		$formatters = $surrogate->formatResults();
 
 		if (!$formatters) {
 			return;
 		}
-
-		$query->formatResults(function($results) use ($formatters) {
-			$property = $this->property();
+		
+		$property = $options['propertyPath'];
+		$query->formatResults(function($results) use ($formatters, $property){
 			$extracted = $results->extract($property)->compile();
 			foreach ($formatters as $callable) {
 				$extracted = new ResultSetDecorator($callable($extracted));
@@ -534,7 +534,7 @@ abstract class Association {
 		$loader->attachAssociations($query, $target, $options['includeFields']);
 		$newBinds = [];
 		foreach ($contain as $alias => $value) {
-			$newBinds[$options['path'] . '.' . $alias] = $value;
+			$newBinds[$options['aliasPath'] . '.' . $alias] = $value;
 		}
 		$query->contain($newBinds);
 	}

+ 14 - 7
src/ORM/EagerLoader.php

@@ -156,7 +156,7 @@ class EagerLoader {
 				$repository,
 				$alias,
 				$options,
-				$alias
+				[]
 			);
 		}
 
@@ -233,7 +233,8 @@ class EagerLoader {
 
 		foreach ($this->attachableAssociations($repository) as $options) {
 			$config = $options['config'] + [
-				'path' => $options['path'],
+				'aliasPath' => $options['aliasPath'],
+				'propertyPath' => $options['propertyPath'],
 				'includeFields' => $includeFields
 			];
 			$options['instance']->attachTo($query, $config);
@@ -285,11 +286,13 @@ class EagerLoader {
  * @param Table $parent owning side of the association
  * @param string $alias name of the association to be loaded
  * @param array $options list of extra options to use for this association
- * @param string $path A dot separated string of associations that lead to this `$alias`
+ * @param array $paths A list of dot separated strings signifying associations that
+ * lead to this `$alias` and the path to follow in entities to fetch a record of each
+ * the association
  * @return array normalized associations
  * @throws \InvalidArgumentException When containments refer to associations that do not exist.
  */
-	protected function _normalizeContain(Table $parent, $alias, $options, $path) {
+	protected function _normalizeContain(Table $parent, $alias, $options, $paths) {
 		$defaults = $this->_containOptions;
 		$instance = $parent->association($alias);
 		if (!$instance) {
@@ -298,6 +301,10 @@ class EagerLoader {
 			);
 		}
 
+		$paths += ['aliasPath' => '', 'propertyPath' => ''];
+		$paths['aliasPath'] .= '.' . $alias;
+		$paths['propertyPath'] .= '.' . $instance->property();
+
 		$table = $instance->target();
 
 		$extra = array_diff_key($options, $defaults);
@@ -305,13 +312,13 @@ class EagerLoader {
 			'associations' => [],
 			'instance' => $instance,
 			'config' => array_diff_key($options, $extra),
-			'path' => $path
+			'aliasPath' => trim($paths['aliasPath'], '.'),
+			'propertyPath' => trim($paths['propertyPath'], '.'),
 		];
 		$config['canBeJoined'] = $instance->canBeJoined($config['config']);
 
 		foreach ($extra as $t => $assoc) {
-			$step = $path . '.' . $t;
-			$config['associations'][$t] = $this->_normalizeContain($table, $t, $assoc, $step);
+			$config['associations'][$t] = $this->_normalizeContain($table, $t, $assoc, $paths);
 		}
 
 		return $config;

+ 13 - 5
tests/TestCase/ORM/EagerLoaderTest.php

@@ -355,19 +355,27 @@ class EagerLoaderTest extends TestCase {
 		$loader = new EagerLoader;
 		$loader->contain($contains);
 		$normalized = $loader->normalized($this->table);
-		$this->assertEquals('clients', $normalized['clients']['path']);
+		$this->assertEquals('clients', $normalized['clients']['aliasPath']);
+		$this->assertEquals('client', $normalized['clients']['propertyPath']);
 
 		$assocs =  $normalized['clients']['associations'];
-		$this->assertEquals('clients.orders', $assocs['orders']['path']);
+		$this->assertEquals('clients.orders', $assocs['orders']['aliasPath']);
+		$this->assertEquals('client.order', $assocs['orders']['propertyPath']);
 
 		$assocs =  $assocs['orders']['associations'];
-		$this->assertEquals('clients.orders.orderTypes', $assocs['orderTypes']['path']);
-		$this->assertEquals('clients.orders.stuff', $assocs['stuff']['path']);
+		$this->assertEquals('clients.orders.orderTypes', $assocs['orderTypes']['aliasPath']);
+		$this->assertEquals('client.order.order_type', $assocs['orderTypes']['propertyPath']);
+		$this->assertEquals('clients.orders.stuff', $assocs['stuff']['aliasPath']);
+		$this->assertEquals('client.order.stuff', $assocs['stuff']['propertyPath']);
 
 		$assocs =  $assocs['stuff']['associations'];
 		$this->assertEquals(
 			'clients.orders.stuff.stuffTypes',
-			$assocs['stuffTypes']['path']
+			$assocs['stuffTypes']['aliasPath']
+		);
+		$this->assertEquals(
+			'client.order.stuff.stuff_type',
+			$assocs['stuffTypes']['propertyPath']
 		);
 	}
 

+ 40 - 0
tests/TestCase/ORM/QueryTest.php

@@ -1615,5 +1615,45 @@ class QueryTest extends TestCase {
 		$this->assertEquals($expected, $results);
 	}
 
+/**
+ * Tests it is possible to apply formatters to deep relations.
+ *
+ * @return void
+ */
+	public function testFormatDeepAssocationRecords() {
+		$table = TableRegistry::get('ArticlesTags');
+		$table->belongsTo('Articles');
+		$table->association('Articles')->target()->belongsTo('Authors');
+
+		$builder = function($q) {
+			return $q
+				->formatResults(function($results) {
+					return $results->map(function($result) {
+						$result->idCopy = $result->id;
+						return $result;
+					});
+				})
+				->formatResults(function($results) {
+					return $results->map(function($result) {
+						$result->idCopy = $result->idCopy + 2;
+						return $result;
+					});
+				});
+		};
+		$query = $table->find()
+			->contain(['Articles' => $builder, 'Articles.Authors' => $builder]);
+		$query->formatResults(function($results) {
+			return $results->map(function($row) {
+				return sprintf(
+					'%s - %s - %s',
+					$row->tag_id,
+					$row->article->idCopy,
+					$row->article->author->idCopy
+				);
+			});
+		});
 
+		$expected = ['1 - 3 - 3', '2 - 3 - 3', '1 - 4 - 5', '3 - 4 - 5'];
+		$this->assertEquals($expected, $query->toArray());
+	}
 }