Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Adding full support for eager loading HasMany when using composite keys
  • Loading branch information
lorenzo committed Jan 13, 2014
1 parent f664429 commit 16f7fe2
Show file tree
Hide file tree
Showing 8 changed files with 274 additions and 13 deletions.
4 changes: 2 additions & 2 deletions Cake/Database/Expression/Comparison.php
Expand Up @@ -162,12 +162,12 @@ protected function _bindValue($generator, $value, $type) {
*
* @param array|\Traversable $value the value to flatten
* @param ValueBinder $generator
* @param string $type the type to cast values to
* @param string|array $type the type to cast values to
* @return string
*/
protected function _flattenValue($value, $generator, $type = null) {
$parts = [];
foreach ($value as $v) {
foreach ($value as $k => $v) {
$parts[] = $this->_bindValue($generator, $v, $type);
}
return implode(',', $parts);
Expand Down
21 changes: 15 additions & 6 deletions Cake/Database/Expression/TupleComparison.php
Expand Up @@ -80,15 +80,24 @@ protected function _stringifyValues($generator) {
continue;
}

$type = isset($this->_type[$i]) ? $this->_type[$i] : null;
if ($this->_isMulti($i, $type)) {
$type = str_replace('[]', '', $type);
$value = $this->_flattenValue($value, $generator, $type);
$values[] = "($value)";
$type = $this->_type;
$multiType = is_array($type);
$isMulti = $this->_isMulti($i, $type);
$type = $isMulti ? str_replace('[]', '', $type) : $type;

if ($isMulti) {
$bound = [];
foreach ($value as $k => $val) {
$valType = $multiType ? $type[$k] : $type;
$bound[] = $this->_bindValue($generator, $val, $valType);
}

$values[] = sprintf('(%s)', implode(',', $bound));
continue;
}

$values[] = $this->_bindValue($generator, $value, $type);
$type = $valType = $multiType ? $type[$i] : $type;
$values[] = $this->_bindValue($generator, $value, $valType);
}

return implode(', ', $values);
Expand Down
10 changes: 8 additions & 2 deletions Cake/ORM/Association/ExternalAssociationTrait.php
Expand Up @@ -266,8 +266,14 @@ protected function _buildQuery($options) {
*/
protected function _addFilteringCondition($query, $key, $filter) {
if (is_array($key)) {
$tuple = new TupleComparison($key, $filter, [], 'IN');
return $query->andWhere($tuple);
$types = [];
$defaults = $query->defaultTypes();
foreach ($key as $k) {
if (isset($defaults[$k])) {
$types[] = $defaults[$k];
}
}
return $query->andWhere(new TupleComparison($key, $filter, $types, 'IN'));
}
return $query->andWhere([$key . ' IN' => $filter]);
}
Expand Down
2 changes: 0 additions & 2 deletions Cake/ORM/Association/HasMany.php
@@ -1,7 +1,5 @@
<?php
/**
* PHP Version 5.4
*
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
*
Expand Down
71 changes: 71 additions & 0 deletions Test/Fixture/SiteArticleFixture.php
@@ -0,0 +1,71 @@
<?php
/**
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* For full copyright and license information, please see the LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @since CakePHP(tm) v 3.0.0
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
namespace Cake\Test\Fixture;

use Cake\TestSuite\Fixture\TestFixture;

class SiteArticleFixture extends TestFixture {

/**
* fields property
*
* @var array
*/
public $fields = [
'id' => ['type' => 'integer'],
'author_id' => ['type' => 'integer', 'null' => true],
'site_id' => ['type' => 'integer', 'null' => true],
'title' => ['type' => 'string', 'null' => true],
'body' => 'text',
'_constraints' => ['primary' => ['type' => 'primary', 'columns' => ['id', 'site_id']]]
];

/**
* records property
*
* @var array
*/
public $records = [
[
'id' => 1,
'author_id' => 1,
'site_id' => 1,
'title' => 'First Article',
'body' => 'First Article Body',
],
[
'id' => 2,
'author_id' => 3,
'site_id' => 2,
'title' => 'Second Article',
'body' => 'Second Article Body',
],
[
'id' => 3,
'author_id' => 1,
'site_id' => 2,
'title' => 'Third Article',
'body' => 'Third Article Body',
],
[
'id' => 4,
'author_id' => 3,
'site_id' => 1,
'title' => 'Fourth Article',
'body' => 'Fourth Article Body',
]
];

}
46 changes: 46 additions & 0 deletions Test/Fixture/SiteAuthorFixture.php
@@ -0,0 +1,46 @@
<?php
/**
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* For full copyright and license information, please see the LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @since CakePHP(tm) v 3.0.0
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
namespace Cake\Test\Fixture;

use Cake\TestSuite\Fixture\TestFixture;

class SiteAuthorFixture extends TestFixture {

/**
* fields property
*
* @var array
*/
public $fields = [
'id' => ['type' => 'integer'],
'name' => ['type' => 'string', 'default' => null],
'site_id' => ['type' => 'integer', 'null' => true],
'_constraints' => ['primary' => ['type' => 'primary', 'columns' => ['id', 'site_id']]]
];

/**
* records property
*
* @var array
*/
public $records = [
['id' => 1, 'name' => 'mark', 'site_id' => 1],
['id' => 2, 'name' => 'juan', 'site_id' => 2],
['id' => 3, 'name' => 'jose', 'site_id' => 2],
['id' => 4, 'name' => 'andy', 'site_id' => 1]
];

}

132 changes: 132 additions & 0 deletions Test/TestCase/ORM/CompositeKeysTest.php
@@ -0,0 +1,132 @@
<?php
/**
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* For full copyright and license information, please see the LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @since CakePHP(tm) v 3.0.0
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
namespace Cake\Test\TestCase\ORM;

use Cake\Database\ConnectionManager;
use Cake\Database\Expression\IdentifierExpression;
use Cake\Database\Expression\OrderByExpression;
use Cake\Database\Expression\QueryExpression;
use Cake\ORM\Query;
use Cake\ORM\ResultSet;
use Cake\ORM\Table;
use Cake\ORM\TableRegistry;
use Cake\TestSuite\TestCase;

/**
* Integration tetss for table operations involving composite keys
*/
class CompositeKeyTest extends TestCase {

/**
* Fixture to be used
*
* @var array
*/
public $fixtures = ['core.site_article', 'core.site_author'];

/**
* setUp method
*
* @return void
*/
public function setUp() {
parent::setUp();
$this->connection = ConnectionManager::get('test');
}

/**
* Data provider for the two types of strategies HasMany implements
*
* @return void
*/
public function strategiesProvider() {
return [['subquery'], ['select']];
}

/**
* Tests that HasMany associations are correctly eager loaded and results
* correctly nested when multiple foreignKeys are used
*
* @dataProvider strategiesProvider
* @return void
*/
public function testHasManyEagerCompositeKeys($strategy) {
$table = TableRegistry::get('SiteAuthors');
TableRegistry::get('SiteArticles');
$table->hasMany('SiteArticles', [
'propertyName' => 'articles',
'strategy' => $strategy,
'sort' => ['SiteArticles.id' => 'asc'],
'foreignKey' => ['author_id', 'site_id']
]);
$query = new Query($this->connection, $table);

$results = $query->select()
->contain('SiteArticles')
->hydrate(false)
->toArray();
$expected = [
[
'id' => 1,
'name' => 'mark',
'site_id' => 1,
'articles' => [
[
'id' => 1,
'title' => 'First Article',
'body' => 'First Article Body',
'author_id' => 1,
'site_id' => 1
]
]
],
[
'id' => 2,
'name' => 'juan',
'site_id' => 2
],
[
'id' => 3,
'name' => 'jose',
'site_id' => 2,
'articles' => [
[
'id' => 2,
'title' => 'Second Article',
'body' => 'Second Article Body',
'author_id' => 3,
'site_id' => 2
]
]
],
[
'id' => 4,
'name' => 'andy',
'site_id' => 1
]
];
$this->assertEquals($expected, $results);

$results = $query->repository($table)
->select()
->contain(['SiteArticles' => ['conditions' => ['id' => 2]]])
->hydrate(false)
->toArray();
unset($expected[0]['articles']);
$this->assertEquals($expected, $results);
$this->assertEquals($table->association('SiteArticles')->strategy(), $strategy);
}

}
1 change: 0 additions & 1 deletion Test/TestCase/ORM/QueryTest.php
Expand Up @@ -16,7 +16,6 @@
*/
namespace Cake\Test\TestCase\ORM;

use Cake\Core\Configure;
use Cake\Database\ConnectionManager;
use Cake\Database\Expression\IdentifierExpression;
use Cake\Database\Expression\OrderByExpression;
Expand Down

0 comments on commit 16f7fe2

Please sign in to comment.