Skip to content

Commit

Permalink
Migrated HasMany to use the new SelectLoader
Browse files Browse the repository at this point in the history
  • Loading branch information
lorenzo committed Oct 10, 2016
1 parent d66a254 commit 4955a51
Show file tree
Hide file tree
Showing 5 changed files with 120 additions and 96 deletions.
23 changes: 14 additions & 9 deletions src/ORM/Association.php
Expand Up @@ -750,6 +750,20 @@ public function deleteAll($conditions)
return $target->deleteAll($expression);
}

/**
* Returns true if the eager loading process will require a set of the owning table's
* binding keys in order to use them as a filter in the finder query.
*
* @param array $options The options containing the strategy to be used.
* @return bool true if a list of keys will be required
*/
public function requiresKeys(array $options = [])
{
$strategy = isset($options['strategy']) ? $options['strategy'] : $this->strategy();

return $strategy === $this::STRATEGY_SELECT;
}

/**
* Triggers beforeFind on the target table for the query this association is
* attaching to
Expand Down Expand Up @@ -1026,15 +1040,6 @@ abstract public function type();
*/
abstract public function eagerLoader(array $options);

/**
* Returns true if the eager loading process will require a set of the owning table's
* binding keys in order to use them as a filter in the finder query.
*
* @param array $options The options containing the strategy to be used.
* @return bool true if a list of keys will be required
*/
abstract function requiresKeys(array $options = []);

/**
* Handles cascading a delete from an associated model.
*
Expand Down
50 changes: 0 additions & 50 deletions src/ORM/Association/BelongsTo.php
Expand Up @@ -202,54 +202,4 @@ public function eagerLoader(array $options) {

return $loader->buildLoadingQuery($options);
}

/**
* {@inheritDoc}
*
* @return bool
*/
public function requiresKeys(array $options = [])
{
$strategy = isset($options['strategy']) ? $options['strategy'] : $this->strategy();

return $strategy === $this::STRATEGY_SELECT;
}

/**
* {@inheritDoc}
*/
protected function _linkField($options)
{
$links = [];
$name = $this->alias();

foreach ((array)$this->bindingKey() 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->bindingKey();

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

return $resultMap;
}
}
106 changes: 85 additions & 21 deletions src/ORM/Association/HasMany.php
Expand Up @@ -20,9 +20,9 @@
use Cake\Database\Expression\QueryExpression;
use Cake\Datasource\EntityInterface;
use Cake\ORM\Association;
use Cake\ORM\Association\Loader\SelectLoader;
use Cake\ORM\Table;
use InvalidArgumentException;
use RuntimeException;
use Traversable;

/**
Expand All @@ -35,7 +35,13 @@ class HasMany extends Association
{

use DependentDeleteTrait;
use ExternalAssociationTrait;

/**
* Order in which target records should be returned
*
* @var mixed
*/
protected $_sort;

/**
* The type of join to be used when adding the association to a query
Expand Down Expand Up @@ -491,37 +497,74 @@ function ($prop) use ($table) {
}

/**
* {@inheritDoc}
* Get the relationship type.
*
* @return string
*/
protected function _linkField($options)
public function type()
{
$links = [];
$name = $this->alias();
if ($options['foreignKey'] === false) {
$msg = 'Cannot have foreignKey = false for hasMany associations. ' .
'You must provide a foreignKey column.';
throw new RuntimeException($msg);
}
return self::ONE_TO_MANY;
}

foreach ((array)$options['foreignKey'] as $key) {
$links[] = sprintf('%s.%s', $name, $key);
}
/**
* Whether this association can be expressed directly in a query join
*
* @param array $options custom options key that could alter the return value
* @return bool if the 'matching' key in $option is true then this function
* will return true, false otherwise
*/
public function canBeJoined(array $options = [])
{
return !empty($options['matching']);
}

if (count($links) === 1) {
return $links[0];
/**
* Sets the name of the field representing the foreign key to the source table.
* If no parameters are passed current field is returned
*
* @param string|null $key the key to be used to link both tables together
* @return string
*/
public function foreignKey($key = null)
{
if ($key === null) {
if ($this->_foreignKey === null) {
$this->_foreignKey = $this->_modelKey($this->source()->table());
}

return $this->_foreignKey;
}

return $links;
return parent::foreignKey($key);
}

/**
* Get the relationship type.
* Sets the sort order in which target records should be returned.
* If no arguments are passed the currently configured value is returned
*
* @return string
* @param mixed $sort A find() compatible order clause
* @return mixed
*/
public function type()
public function sort($sort = null)
{
return self::ONE_TO_MANY;
if ($sort !== null) {
$this->_sort = $sort;
}

return $this->_sort;
}

/**
* {@inheritDoc}
*/
public function defaultRowValue($row, $joined)
{
$sourceAlias = $this->source()->alias();
if (isset($row[$sourceAlias])) {
$row[$sourceAlias][$this->property()] = $joined ? null : [];
}

return $row;
}

/**
Expand All @@ -539,4 +582,25 @@ protected function _options(array $opts)
$this->sort($opts['sort']);
}
}

/**
* {@inheritDoc}
*
* @return callable
*/
public function eagerLoader(array $options) {
$loader = new SelectLoader([
'alias' => $this->alias(),
'sourceAlias' => $this->source()->alias(),
'targetAlias' => $this->target()->alias(),
'foreignKey' => $this->foreignKey(),
'bindingKey' => $this->bindingKey(),
'strategy' => $this->strategy(),
'associationType' => $this->type(),
'sort' => $this->sort(),
'finder' => [$this, 'find']
]);

return $loader->buildLoadingQuery($options);
}
}
12 changes: 0 additions & 12 deletions src/ORM/Association/HasOne.php
Expand Up @@ -147,16 +147,4 @@ public function eagerLoader(array $options) {

return $loader->buildLoadingQuery($options);
}

/**
* {@inheritDoc}
*
* @return bool
*/
public function requiresKeys(array $options = [])
{
$strategy = isset($options['strategy']) ? $options['strategy'] : $this->strategy();

return $strategy === $this::STRATEGY_SELECT;
}
}
25 changes: 21 additions & 4 deletions src/ORM/Association/Loader/SelectLoader.php
Expand Up @@ -19,6 +19,7 @@
use Cake\Database\ValueBinder;
use InvalidArgumentException;
use Cake\ORM\Association;
use RuntimeException;

/**
* Implements the logic for loading an association using a SELECT query
Expand All @@ -42,6 +43,8 @@ class SelectLoader

protected $associationType;

protected $sort;

public function __construct(array $options)
{
$this->alias = $options['alias'];
Expand All @@ -52,6 +55,7 @@ public function __construct(array $options)
$this->bindingKey = $options['bindingKey'];
$this->finder = $options['finder'];
$this->associationType = $options['associationType'];
$this->sort = isset($options['sort']) ? $options['sort'] : null;
}


Expand All @@ -75,7 +79,8 @@ protected function _defaultOptions()
'foreignKey' => $this->foreignKey,
'conditions' => [],
'strategy' => $this->strategy,
'nestKey' => $this->alias
'nestKey' => $this->alias,
'sort' => $this->sort
];
}

Expand Down Expand Up @@ -265,7 +270,14 @@ protected function _linkField($options)
{
$links = [];
$name = $this->alias;
$keys = $this->associationType === Association::ONE_TO_ONE ?

if ($options['foreignKey'] === false && $this->associationType === Association::ONE_TO_MANY) {
$msg = 'Cannot have foreignKey = false for hasMany associations. ' .
'You must provide a foreignKey column.';
throw new RuntimeException($msg);
}

$keys = in_array($this->associationType, [Association::ONE_TO_ONE, Association::ONE_TO_MANY]) ?
$this->foreignKey :
$this->bindingKey;

Expand Down Expand Up @@ -354,7 +366,8 @@ protected function _subqueryFields($query)
protected function _buildResultMap($fetchQuery, $options)
{
$resultMap = [];
$keys = $this->associationType === Association::ONE_TO_ONE ?
$singleResult = in_array($this->associationType, [Association::MANY_TO_ONE, Association::ONE_TO_ONE]);
$keys = in_array($this->associationType, [Association::ONE_TO_ONE, Association::ONE_TO_MANY]) ?
$this->foreignKey :
$this->bindingKey;
$key = (array)$keys;
Expand All @@ -364,7 +377,11 @@ protected function _buildResultMap($fetchQuery, $options)
foreach ($key as $k) {
$values[] = $result[$k];
}
$resultMap[implode(';', $values)] = $result;
if ($singleResult) {
$resultMap[implode(';', $values)] = $result;
} else {
$resultMap[implode(';', $values)][] = $result;
}
}

return $resultMap;
Expand Down

0 comments on commit 4955a51

Please sign in to comment.