Permalink
Browse files

Added support for using formatResult on deep attachable associations

  • Loading branch information...
1 parent 7f64c8a commit ec0cc6a8b364be933861b8f3a5d7c01f9afc412f @lorenzo lorenzo committed Feb 6, 2014
Showing with 73 additions and 18 deletions.
  1. +6 −6 src/ORM/Association.php
  2. +14 −7 src/ORM/EagerLoader.php
  3. +13 −5 tests/TestCase/ORM/EagerLoaderTest.php
  4. +40 −0 tests/TestCase/ORM/QueryTest.php
View
@@ -430,7 +430,7 @@ public function attachTo(Query $query, array $options = []) {
$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 @@ protected function _appendFields($query, $surrogate, $options) {
}
}
- 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 @@ protected function _bindNewAssociations($query, $surrogate, $options) {
$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);
}
View
@@ -156,7 +156,7 @@ public function normalized(Table $repository) {
$repository,
$alias,
$options,
- $alias
+ []
);
}
@@ -233,7 +233,8 @@ public function attachAssociations(Query $query, Table $repository, $includeFiel
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 @@ public function externalAssociations(Table $repository) {
* @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,20 +301,24 @@ protected function _normalizeContain(Table $parent, $alias, $options, $path) {
);
}
+ $paths += ['aliasPath' => '', 'propertyPath' => ''];
+ $paths['aliasPath'] .= '.' . $alias;
+ $paths['propertyPath'] .= '.' . $instance->property();
+
$table = $instance->target();
$extra = array_diff_key($options, $defaults);
$config = [
'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;
@@ -355,19 +355,27 @@ public function testNormalizedPath() {
$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']
);
}
@@ -1615,5 +1615,45 @@ public function testFormatBelongsToRecords() {
$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());
+ }
}

0 comments on commit ec0cc6a

Please sign in to comment.