Skip to content

Commit

Permalink
Implemented the select and subquery strategy for BelongsTo
Browse files Browse the repository at this point in the history
  • Loading branch information
lorenzo committed Apr 1, 2014
1 parent 9c14bec commit e6f3e8e
Show file tree
Hide file tree
Showing 5 changed files with 153 additions and 42 deletions.
52 changes: 51 additions & 1 deletion src/ORM/Association/BelongsTo.php
Expand Up @@ -16,6 +16,7 @@

use Cake\Database\Expression\IdentifierExpression;
use Cake\ORM\Association;
use Cake\ORM\Association\SelectableAssociationTrait;
use Cake\ORM\Entity;
use Cake\ORM\Table;
use Cake\Utility\Inflector;
Expand All @@ -28,6 +29,8 @@
*/
class BelongsTo extends Association {

use SelectableAssociationTrait;

/**
* Sets the name of the field representing the foreign key to the target table.
* If no parameters are passed current field is returned
Expand Down Expand Up @@ -90,6 +93,23 @@ public function isOwningSide(Table $side) {
return $side === $this->target();
}

/**
* {@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
Expand Down Expand Up @@ -157,7 +177,37 @@ protected function _joinCondition(array $options) {
* {@inheritdoc}
*
*/
public function eagerLoader(array $options) {
protected function _linkField($options) {
$links = [];
$name = $this->name();

foreach ((array)$this->target()->primaryKey() as $key) {
$links[] = sprintf('%s.%s', $name, $key);
}

if (count($links) === 1) {
return $links[0];
}

return $links;
}

/**
* {@inheritdoc}
*
*/
protected function _buildResultMap($fetchQuery, $options) {
$resultMap = [];
$key = (array)$this->target()->primaryKey();

foreach ($fetchQuery->all() as $result) {
$values = [];
foreach ($key as $k) {
$values[] = $result[$k];
}
$resultMap[implode(';', $values)] = $result;
}
return $resultMap;
}

}
42 changes: 41 additions & 1 deletion src/ORM/Association/ExternalAssociationTrait.php
Expand Up @@ -25,7 +25,9 @@
*/
trait ExternalAssociationTrait {

use SelectableAssociationTrait;
use SelectableAssociationTrait {
_defaultOptions as private _selectableOptions;
}

/**
* Order in which target records should be returned
Expand Down Expand Up @@ -100,6 +102,44 @@ public function transformRow($row, $joined) {
return $row;
}

/**
* Returns the default options to use for the eagerLoader
*
* @return array
*/
protected function _defaultOptions() {
return $this->_selectableOptions() + [
'sort' => $this->sort()
];
}

/**
* {@inheritdoc}
*
*/
protected function _buildResultMap($fetchQuery, $options) {
$resultMap = [];
$key = (array)$options['foreignKey'];

foreach ($fetchQuery->all() as $result) {
$values = [];
foreach ($key as $k) {
$values[] = $result[$k];
}
$resultMap[implode(';', $values)][] = $result;
}
return $resultMap;
}

/**
* Returns the key under which the eagerLoader will put this association results
*
* @return void
*/
protected function _nestingKey() {
return $this->_name . '___collection_';
}

/**
* Returns a single or multiple conditions to be appended to the generated join
* clause for getting the results on the target table.
Expand Down
19 changes: 19 additions & 0 deletions src/ORM/Association/HasMany.php
Expand Up @@ -117,6 +117,25 @@ public function save(Entity $entity, $options = []) {
return $entity;
}

/**
* {@inheritdoc}
*
*/
protected function _linkField($options) {
$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;
}

/**
* Get the relationship type.
*
Expand Down
68 changes: 30 additions & 38 deletions src/ORM/Association/SelectableAssociationTrait.php
Expand Up @@ -38,13 +38,7 @@ public function requiresKeys($options = []) {
*
*/
public function eagerLoader(array $options) {
$options += [
'foreignKey' => $this->foreignKey(),
'conditions' => [],
'sort' => $this->sort(),
'strategy' => $this->strategy()
];

$options = $options + $this->_defaultOptions();
$queryBuilder = false;
if (!empty($options['queryBuilder'])) {
$queryBuilder = $options['queryBuilder'];
Expand All @@ -59,6 +53,19 @@ public function eagerLoader(array $options) {
return $this->_resultInjector($fetchQuery, $resultMap);
}

/**
* Returns the default options to use for the eagerLoader
*
* @return array
*/
protected function _defaultOptions() {
return [
'foreignKey' => $this->foreignKey(),
'conditions' => [],
'strategy' => $this->strategy()
];
}

/**
* 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
Expand Down Expand Up @@ -141,20 +148,7 @@ protected function _addFilteringCondition($query, $key, $filter) {
* @param array $options
* @return string
*/
protected function _linkField($options) {
$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;
}
protected abstract function _linkField($options);

/**
* Builds a query to be used as a condition for filtering records in in the
Expand Down Expand Up @@ -187,19 +181,7 @@ protected function _buildSubquery($query) {
* @param array $options The options passed to the eager loader
* @return array
*/
protected function _buildResultMap($fetchQuery, $options) {
$resultMap = [];
$key = (array)$options['foreignKey'];

foreach ($fetchQuery->all() as $result) {
$values = [];
foreach ($key as $k) {
$values[] = $result[$k];
}
$resultMap[implode(';', $values)][] = $result;
}
return $resultMap;
}
protected abstract function _buildResultMap($fetchQuery, $options);

/**
* Returns a callable to be used for each row in a query result set
Expand All @@ -213,15 +195,16 @@ protected function _buildResultMap($fetchQuery, $options) {
protected function _resultInjector($fetchQuery, $resultMap) {
$source = $this->source();
$sAlias = $source->alias();
$tAlias = $this->target()->alias();
$keys = $this->type() === $this::ONE_TO_ONE ?
$this->foreignKey() :
$source->primaryKey();

$sourceKeys = [];
foreach ((array)$source->primaryKey() as $key) {
foreach ((array)$keys as $key) {
$sourceKeys[] = key($fetchQuery->aliasField($key, $sAlias));
}

$nestKey = $tAlias . '___collection_';

$nestKey = $this->_nestingKey();
if (count($sourceKeys) > 1) {
return $this->_multiKeysInjector($resultMap, $sourceKeys, $nestKey);
}
Expand All @@ -235,6 +218,15 @@ protected function _resultInjector($fetchQuery, $resultMap) {
};
}

/**
* 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
Expand Down
14 changes: 12 additions & 2 deletions tests/TestCase/ORM/QueryTest.php
Expand Up @@ -97,15 +97,25 @@ public function tearDown() {
TableRegistry::clear();
}

/**
* Provides strategies for associations that can be joined
*
* @return void
*/
public function internalStategiesProvider() {
return [['join'], ['select'], ['subquery']];
}

/**
* Tests that results are grouped correctly when using contain()
* and results are not hydrated
*
* @dataProvider internalStategiesProvider
* @return void
*/
public function testContainResultFetchingOneLevel() {
public function testContainResultFetchingOneLevel($strategy) {
$table = TableRegistry::get('articles', ['table' => 'articles']);
$table->belongsTo('authors');
$table->belongsTo('authors', ['strategy' => $strategy]);

$query = new Query($this->connection, $table);
$results = $query->select()
Expand Down

0 comments on commit e6f3e8e

Please sign in to comment.