From ec0cc6a8b364be933861b8f3a5d7c01f9afc412f Mon Sep 17 00:00:00 2001 From: Jose Lorenzo Rodriguez Date: Fri, 7 Feb 2014 00:00:19 +0100 Subject: [PATCH] Added support for using formatResult on deep attachable associations --- src/ORM/Association.php | 12 ++++---- src/ORM/EagerLoader.php | 21 +++++++++----- tests/TestCase/ORM/EagerLoaderTest.php | 18 ++++++++---- tests/TestCase/ORM/QueryTest.php | 40 ++++++++++++++++++++++++++ 4 files changed, 73 insertions(+), 18 deletions(-) diff --git a/src/ORM/Association.php b/src/ORM/Association.php index 3251b89c6f1..f1e4c15f014 100644 --- a/src/ORM/Association.php +++ b/src/ORM/Association.php @@ -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); } diff --git a/src/ORM/EagerLoader.php b/src/ORM/EagerLoader.php index 23d4fa2ea03..89c4b5467de 100644 --- a/src/ORM/EagerLoader.php +++ b/src/ORM/EagerLoader.php @@ -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,6 +301,10 @@ 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); @@ -305,13 +312,13 @@ protected function _normalizeContain(Table $parent, $alias, $options, $path) { '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; diff --git a/tests/TestCase/ORM/EagerLoaderTest.php b/tests/TestCase/ORM/EagerLoaderTest.php index ec65836f409..f66dc0f391c 100644 --- a/tests/TestCase/ORM/EagerLoaderTest.php +++ b/tests/TestCase/ORM/EagerLoaderTest.php @@ -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'] ); } diff --git a/tests/TestCase/ORM/QueryTest.php b/tests/TestCase/ORM/QueryTest.php index 40638b61686..391492b71d3 100644 --- a/tests/TestCase/ORM/QueryTest.php +++ b/tests/TestCase/ORM/QueryTest.php @@ -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()); + } }