Skip to content

Commit 16f7fe2

Browse files
committed
Adding full support for eager loading HasMany when using composite keys
1 parent f664429 commit 16f7fe2

File tree

8 files changed

+274
-13
lines changed

8 files changed

+274
-13
lines changed

Cake/Database/Expression/Comparison.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -162,12 +162,12 @@ protected function _bindValue($generator, $value, $type) {
162162
*
163163
* @param array|\Traversable $value the value to flatten
164164
* @param ValueBinder $generator
165-
* @param string $type the type to cast values to
165+
* @param string|array $type the type to cast values to
166166
* @return string
167167
*/
168168
protected function _flattenValue($value, $generator, $type = null) {
169169
$parts = [];
170-
foreach ($value as $v) {
170+
foreach ($value as $k => $v) {
171171
$parts[] = $this->_bindValue($generator, $v, $type);
172172
}
173173
return implode(',', $parts);

Cake/Database/Expression/TupleComparison.php

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -80,15 +80,24 @@ protected function _stringifyValues($generator) {
8080
continue;
8181
}
8282

83-
$type = isset($this->_type[$i]) ? $this->_type[$i] : null;
84-
if ($this->_isMulti($i, $type)) {
85-
$type = str_replace('[]', '', $type);
86-
$value = $this->_flattenValue($value, $generator, $type);
87-
$values[] = "($value)";
83+
$type = $this->_type;
84+
$multiType = is_array($type);
85+
$isMulti = $this->_isMulti($i, $type);
86+
$type = $isMulti ? str_replace('[]', '', $type) : $type;
87+
88+
if ($isMulti) {
89+
$bound = [];
90+
foreach ($value as $k => $val) {
91+
$valType = $multiType ? $type[$k] : $type;
92+
$bound[] = $this->_bindValue($generator, $val, $valType);
93+
}
94+
95+
$values[] = sprintf('(%s)', implode(',', $bound));
8896
continue;
8997
}
9098

91-
$values[] = $this->_bindValue($generator, $value, $type);
99+
$type = $valType = $multiType ? $type[$i] : $type;
100+
$values[] = $this->_bindValue($generator, $value, $valType);
92101
}
93102

94103
return implode(', ', $values);

Cake/ORM/Association/ExternalAssociationTrait.php

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -266,8 +266,14 @@ protected function _buildQuery($options) {
266266
*/
267267
protected function _addFilteringCondition($query, $key, $filter) {
268268
if (is_array($key)) {
269-
$tuple = new TupleComparison($key, $filter, [], 'IN');
270-
return $query->andWhere($tuple);
269+
$types = [];
270+
$defaults = $query->defaultTypes();
271+
foreach ($key as $k) {
272+
if (isset($defaults[$k])) {
273+
$types[] = $defaults[$k];
274+
}
275+
}
276+
return $query->andWhere(new TupleComparison($key, $filter, $types, 'IN'));
271277
}
272278
return $query->andWhere([$key . ' IN' => $filter]);
273279
}

Cake/ORM/Association/HasMany.php

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
<?php
22
/**
3-
* PHP Version 5.4
4-
*
53
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
64
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
75
*

Test/Fixture/SiteArticleFixture.php

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
<?php
2+
/**
3+
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
4+
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
5+
*
6+
* Licensed under The MIT License
7+
* For full copyright and license information, please see the LICENSE.txt
8+
* Redistributions of files must retain the above copyright notice.
9+
*
10+
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
11+
* @link http://cakephp.org CakePHP(tm) Project
12+
* @since CakePHP(tm) v 3.0.0
13+
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
14+
*/
15+
namespace Cake\Test\Fixture;
16+
17+
use Cake\TestSuite\Fixture\TestFixture;
18+
19+
class SiteArticleFixture extends TestFixture {
20+
21+
/**
22+
* fields property
23+
*
24+
* @var array
25+
*/
26+
public $fields = [
27+
'id' => ['type' => 'integer'],
28+
'author_id' => ['type' => 'integer', 'null' => true],
29+
'site_id' => ['type' => 'integer', 'null' => true],
30+
'title' => ['type' => 'string', 'null' => true],
31+
'body' => 'text',
32+
'_constraints' => ['primary' => ['type' => 'primary', 'columns' => ['id', 'site_id']]]
33+
];
34+
35+
/**
36+
* records property
37+
*
38+
* @var array
39+
*/
40+
public $records = [
41+
[
42+
'id' => 1,
43+
'author_id' => 1,
44+
'site_id' => 1,
45+
'title' => 'First Article',
46+
'body' => 'First Article Body',
47+
],
48+
[
49+
'id' => 2,
50+
'author_id' => 3,
51+
'site_id' => 2,
52+
'title' => 'Second Article',
53+
'body' => 'Second Article Body',
54+
],
55+
[
56+
'id' => 3,
57+
'author_id' => 1,
58+
'site_id' => 2,
59+
'title' => 'Third Article',
60+
'body' => 'Third Article Body',
61+
],
62+
[
63+
'id' => 4,
64+
'author_id' => 3,
65+
'site_id' => 1,
66+
'title' => 'Fourth Article',
67+
'body' => 'Fourth Article Body',
68+
]
69+
];
70+
71+
}

Test/Fixture/SiteAuthorFixture.php

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
<?php
2+
/**
3+
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
4+
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
5+
*
6+
* Licensed under The MIT License
7+
* For full copyright and license information, please see the LICENSE.txt
8+
* Redistributions of files must retain the above copyright notice.
9+
*
10+
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
11+
* @link http://cakephp.org CakePHP(tm) Project
12+
* @since CakePHP(tm) v 3.0.0
13+
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
14+
*/
15+
namespace Cake\Test\Fixture;
16+
17+
use Cake\TestSuite\Fixture\TestFixture;
18+
19+
class SiteAuthorFixture extends TestFixture {
20+
21+
/**
22+
* fields property
23+
*
24+
* @var array
25+
*/
26+
public $fields = [
27+
'id' => ['type' => 'integer'],
28+
'name' => ['type' => 'string', 'default' => null],
29+
'site_id' => ['type' => 'integer', 'null' => true],
30+
'_constraints' => ['primary' => ['type' => 'primary', 'columns' => ['id', 'site_id']]]
31+
];
32+
33+
/**
34+
* records property
35+
*
36+
* @var array
37+
*/
38+
public $records = [
39+
['id' => 1, 'name' => 'mark', 'site_id' => 1],
40+
['id' => 2, 'name' => 'juan', 'site_id' => 2],
41+
['id' => 3, 'name' => 'jose', 'site_id' => 2],
42+
['id' => 4, 'name' => 'andy', 'site_id' => 1]
43+
];
44+
45+
}
46+
Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
<?php
2+
/**
3+
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
4+
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
5+
*
6+
* Licensed under The MIT License
7+
* For full copyright and license information, please see the LICENSE.txt
8+
* Redistributions of files must retain the above copyright notice.
9+
*
10+
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
11+
* @link http://cakephp.org CakePHP(tm) Project
12+
* @since CakePHP(tm) v 3.0.0
13+
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
14+
*/
15+
namespace Cake\Test\TestCase\ORM;
16+
17+
use Cake\Database\ConnectionManager;
18+
use Cake\Database\Expression\IdentifierExpression;
19+
use Cake\Database\Expression\OrderByExpression;
20+
use Cake\Database\Expression\QueryExpression;
21+
use Cake\ORM\Query;
22+
use Cake\ORM\ResultSet;
23+
use Cake\ORM\Table;
24+
use Cake\ORM\TableRegistry;
25+
use Cake\TestSuite\TestCase;
26+
27+
/**
28+
* Integration tetss for table operations involving composite keys
29+
*/
30+
class CompositeKeyTest extends TestCase {
31+
32+
/**
33+
* Fixture to be used
34+
*
35+
* @var array
36+
*/
37+
public $fixtures = ['core.site_article', 'core.site_author'];
38+
39+
/**
40+
* setUp method
41+
*
42+
* @return void
43+
*/
44+
public function setUp() {
45+
parent::setUp();
46+
$this->connection = ConnectionManager::get('test');
47+
}
48+
49+
/**
50+
* Data provider for the two types of strategies HasMany implements
51+
*
52+
* @return void
53+
*/
54+
public function strategiesProvider() {
55+
return [['subquery'], ['select']];
56+
}
57+
58+
/**
59+
* Tests that HasMany associations are correctly eager loaded and results
60+
* correctly nested when multiple foreignKeys are used
61+
*
62+
* @dataProvider strategiesProvider
63+
* @return void
64+
*/
65+
public function testHasManyEagerCompositeKeys($strategy) {
66+
$table = TableRegistry::get('SiteAuthors');
67+
TableRegistry::get('SiteArticles');
68+
$table->hasMany('SiteArticles', [
69+
'propertyName' => 'articles',
70+
'strategy' => $strategy,
71+
'sort' => ['SiteArticles.id' => 'asc'],
72+
'foreignKey' => ['author_id', 'site_id']
73+
]);
74+
$query = new Query($this->connection, $table);
75+
76+
$results = $query->select()
77+
->contain('SiteArticles')
78+
->hydrate(false)
79+
->toArray();
80+
$expected = [
81+
[
82+
'id' => 1,
83+
'name' => 'mark',
84+
'site_id' => 1,
85+
'articles' => [
86+
[
87+
'id' => 1,
88+
'title' => 'First Article',
89+
'body' => 'First Article Body',
90+
'author_id' => 1,
91+
'site_id' => 1
92+
]
93+
]
94+
],
95+
[
96+
'id' => 2,
97+
'name' => 'juan',
98+
'site_id' => 2
99+
],
100+
[
101+
'id' => 3,
102+
'name' => 'jose',
103+
'site_id' => 2,
104+
'articles' => [
105+
[
106+
'id' => 2,
107+
'title' => 'Second Article',
108+
'body' => 'Second Article Body',
109+
'author_id' => 3,
110+
'site_id' => 2
111+
]
112+
]
113+
],
114+
[
115+
'id' => 4,
116+
'name' => 'andy',
117+
'site_id' => 1
118+
]
119+
];
120+
$this->assertEquals($expected, $results);
121+
122+
$results = $query->repository($table)
123+
->select()
124+
->contain(['SiteArticles' => ['conditions' => ['id' => 2]]])
125+
->hydrate(false)
126+
->toArray();
127+
unset($expected[0]['articles']);
128+
$this->assertEquals($expected, $results);
129+
$this->assertEquals($table->association('SiteArticles')->strategy(), $strategy);
130+
}
131+
132+
}

Test/TestCase/ORM/QueryTest.php

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616
*/
1717
namespace Cake\Test\TestCase\ORM;
1818

19-
use Cake\Core\Configure;
2019
use Cake\Database\ConnectionManager;
2120
use Cake\Database\Expression\IdentifierExpression;
2221
use Cake\Database\Expression\OrderByExpression;

0 commit comments

Comments
 (0)