Permalink
Browse files

Adding full support for eager loading HasMany when using composite keys

  • Loading branch information...
1 parent f664429 commit 16f7fe222b55656e9eb084da1b4efd56d4ac0fc8 @lorenzo lorenzo committed Jan 13, 2014
@@ -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);
@@ -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);
@@ -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]);
}
@@ -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)
*
@@ -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',
+ ]
+ ];
+
+}
@@ -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]
+ ];
+
+}
+
@@ -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);
+ }
+
+}
@@ -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;

0 comments on commit 16f7fe2

Please sign in to comment.