Permalink
Browse files

Implemented eager loading for HasMany and multiple foreignKeys

  • Loading branch information...
1 parent cd086e8 commit 75ab7776a47cc9d41fe9bc7b9b28b473b0cdaa30 @lorenzo lorenzo committed Jan 12, 2014
@@ -17,6 +17,7 @@
namespace Cake\ORM\Association;
use Cake\Database\Expression\IdentifierExpression;
+use Cake\Database\Expression\TupleComparison;
use Cake\ORM\Query;
use Cake\Utility\Inflector;
@@ -164,8 +165,13 @@ protected function _resultInjector($fetchQuery, $resultMap) {
$sourceKeys[] = key($fetchQuery->aliasField($key, $sAlias));
}
- $sourceKey = implode(';', $sourceKeys);
$nestKey = $tAlias . '__' . $tAlias;
+
+ if (count($sourceKeys) > 1) {
+ return $this->_multiKeysInjector($resultMap, $sourceKeys, $nestKey);
+ }
+
+ $sourceKey = $sourceKeys[0];
return function($row) use ($resultMap, $sourceKey, $nestKey) {
if (isset($resultMap[$row[$sourceKey]])) {
$row[$nestKey] = $resultMap[$row[$sourceKey]];
@@ -174,6 +180,21 @@ protected function _resultInjector($fetchQuery, $resultMap) {
};
}
+ protected function _multiKeysInjector($resultMap, $sourceKeys, $nestKey) {
+ return function($row) use ($resultMap, $sourceKeys, $nestKey) {
+ $values = [];
+ foreach ($sourceKeys as $key) {
+ $values[] = $row[$key];
+ }
+
+ $key = implode(';', $values);
+ if (isset($resultMap[$key])) {
+ $row[$nestKey] = $resultMap[$key];
+ }
+ return $row;
+ };
+ }
+
/**
* Auxiliary function to construct a new Query object to return all the records
* in the target table that are associated to those specified in $options from
@@ -234,6 +255,10 @@ protected function _buildQuery($options) {
* @return \Cake\ORM\Query
*/
protected function _addFilteringCondition($query, $key, $filter) {
+ if (is_array($key)) {
+ $tuple = new TupleComparison($key, $filter, [], 'IN');
+ return $query->andWhere($tuple);
+ }
return $query->andWhere([$key . ' IN' => $filter]);
}
@@ -245,7 +270,18 @@ protected function _addFilteringCondition($query, $key, $filter) {
* @return string
*/
protected function _linkField($options) {
- return sprintf('%s.%s', $this->name(), $options['foreignKey']);
+ $links = [];
+ $name = $this->name();
+
+ foreach ((array)$options['foreignKey'] as $key) {
+ $links[] = sprintf('%s.%s', $name, $key);
+ }
+
+ if (count($links) === 1) {
+ return $links[0];
+ }
+
+ return $links;
}
/**
@@ -91,9 +91,14 @@ public function eagerLoader(array $options) {
}
$resultMap = [];
- $key = $options['foreignKey'];
+ $key = (array)$options['foreignKey'];
+
foreach ($fetchQuery->all() as $result) {
- $resultMap[$result[$key]][] = $result;
+ $values = [];
+ foreach ($key as $k) {
+ $values[] = $result[$k];
+ }
+ $resultMap[implode(';', $values)][] = $result;
}
return $this->_resultInjector($fetchQuery, $resultMap);
@@ -415,6 +415,48 @@ public function testEagerLoaderWithQueryBuilder() {
}
/**
+ * Test the eager loader method with no extra options
+ *
+ * @return void
+ */
+ public function testEagerLoaderMultipleKeys() {
+ $config = [
+ 'sourceTable' => $this->author,
+ 'targetTable' => $this->article,
+ 'strategy' => 'select',
+ 'foreignKey' => ['author_id', 'site_id']
+ ];
+
+ $this->author->primaryKey(['id', 'site_id']);
+ $association = new HasMany('Articles', $config);
+ $keys = [[1, 10], [2, 20], [3, 30], [4, 40]];
+ $query = $this->getMock('Cake\ORM\Query', ['all'], [null, null]);
+ $this->article->expects($this->once())->method('find')->with('all')
+ ->will($this->returnValue($query));
+ $results = [
+ ['id' => 1, 'title' => 'article 1', 'author_id' => 2, 'site_id' => 10],
+ ['id' => 2, 'title' => 'article 2', 'author_id' => 1, 'site_id' => 20]
+ ];
+ $query->expects($this->once())->method('all')
+ ->will($this->returnValue($results));
+
+ $callable = $association->eagerLoader(compact('keys', 'query'));
+ $row = ['Authors__id' => 2, 'Authors__site_id' => 10, 'username' => 'author 1'];
+ $result = $callable($row);
+ $row['Articles__Articles'] = [
+ ['id' => 1, 'title' => 'article 1', 'author_id' => 2, 'site_id' => 10]
+ ];
+ $this->assertEquals($row, $result);
+
+ $row = ['Authors__id' => 1, 'username' => 'author 2', 'Authors__site_id' => 20];
+ $result = $callable($row);
+ $row['Articles__Articles'] = [
+ ['id' => 2, 'title' => 'article 2', 'author_id' => 1, 'site_id' => 20]
+ ];
+ $this->assertEquals($row, $result);
+ }
+
+/**
* Tests that the correct join and fields are attached to a query depending on
* the association config
*

0 comments on commit 75ab777

Please sign in to comment.