diff --git a/Cake/ORM/Association.php b/Cake/ORM/Association.php index 8f1ac1cbaf8..e81dcb5eabc 100644 --- a/Cake/ORM/Association.php +++ b/Cake/ORM/Association.php @@ -393,6 +393,7 @@ public function attachTo(Query $query, array $options = []) { 'includeFields' => true, 'foreignKey' => $this->foreignKey(), 'conditions' => [], + 'fields' => [], 'type' => $this->joinType(), 'table' => $target->table() ]; @@ -405,6 +406,14 @@ public function attachTo(Query $query, array $options = []) { } } + $options['conditions'] = $query->newExpr()->add($options['conditions']); + + if (!empty($options['queryBuilder'])) { + $newQuery = $options['queryBuilder']($target->query()); + $options['fields'] = $newQuery->clause('select') ?: $options['fields']; + $options['conditions']->add($newQuery->clause('where')); + } + $joinOptions = ['table' => 1, 'conditions' => 1, 'type' => 1]; $query->join([$target->alias() => array_intersect_key($options, $joinOptions)]); diff --git a/Cake/ORM/Association/BelongsToMany.php b/Cake/ORM/Association/BelongsToMany.php index d002fd39d5c..d004c1c5055 100644 --- a/Cake/ORM/Association/BelongsToMany.php +++ b/Cake/ORM/Association/BelongsToMany.php @@ -249,6 +249,11 @@ public function eagerLoader(array $options) { 'strategy' => $this->strategy() ]; $fetchQuery = $this->_buildQuery($options); + + if (!empty($options['queryBuilder'])) { + $fetchQuery = $options['queryBuilder']($fetchQuery); + } + $resultMap = []; $key = $options['foreignKey']; $property = $this->target()->association($this->junction()->alias())->property(); diff --git a/Cake/ORM/Association/ExternalAssociationTrait.php b/Cake/ORM/Association/ExternalAssociationTrait.php index 8683d65c3c5..58cb053439e 100644 --- a/Cake/ORM/Association/ExternalAssociationTrait.php +++ b/Cake/ORM/Association/ExternalAssociationTrait.php @@ -205,6 +205,10 @@ protected function _buildQuery($options) { $fetchQuery->contain($options['contain']); } + if (!empty($options['queryBuilder'])) { + $options['queryBuilder']($fetchQuery); + } + return $fetchQuery; } diff --git a/Cake/ORM/Association/HasMany.php b/Cake/ORM/Association/HasMany.php index 3a7c63b10cc..7a432cd9a9b 100644 --- a/Cake/ORM/Association/HasMany.php +++ b/Cake/ORM/Association/HasMany.php @@ -85,6 +85,11 @@ public function eagerLoader(array $options) { 'strategy' => $this->strategy() ]; $fetchQuery = $this->_buildQuery($options); + + if (!empty($options['queryBuilder'])) { + $fetchQuery = $options['queryBuilder']($fetchQuery); + } + $resultMap = []; $key = $options['foreignKey']; foreach ($fetchQuery->all() as $result) { diff --git a/Cake/Test/TestCase/ORM/Association/BelongsToManyTest.php b/Cake/Test/TestCase/ORM/Association/BelongsToManyTest.php index e2080ef4bc4..d257c783e13 100644 --- a/Cake/Test/TestCase/ORM/Association/BelongsToManyTest.php +++ b/Cake/Test/TestCase/ORM/Association/BelongsToManyTest.php @@ -17,6 +17,7 @@ namespace Cake\Test\TestCase\ORM\Association; use Cake\Database\Expression\IdentifierExpression; +use Cake\Database\Expression\QueryExpression; use Cake\ORM\Association\BelongsToMany; use Cake\ORM\Entity; use Cake\ORM\Query; @@ -221,9 +222,9 @@ public function testAttachTo() { $association = new BelongsToMany('Tags', $config); $query->expects($this->at(0))->method('join')->with([ 'Tags' => [ - 'conditions' => [ + 'conditions' => new QueryExpression([ 'Tags.name' => 'cake' - ], + ]), 'type' => 'INNER', 'table' => 'tags' ] @@ -234,10 +235,10 @@ public function testAttachTo() { $query->expects($this->at(2))->method('join')->with([ 'ArticlesTags' => [ - 'conditions' => [ + 'conditions' => new QueryExpression([ ['Articles.id' => $field1], ['Tags.id' => $field2] - ], + ]), 'type' => 'INNER', 'table' => 'articles_tags' ] @@ -275,9 +276,9 @@ public function testAttachToNoFields() { $association = new BelongsToMany('Tags', $config); $query->expects($this->at(0))->method('join')->with([ 'Tags' => [ - 'conditions' => [ + 'conditions' => new QueryExpression([ 'Tags.name' => 'cake' - ], + ]), 'type' => 'INNER', 'table' => 'tags' ] @@ -288,10 +289,10 @@ public function testAttachToNoFields() { $query->expects($this->at(1))->method('join')->with([ 'ArticlesTags' => [ - 'conditions' => [ + 'conditions' => new QueryExpression([ ['Articles.id' => $field1], ['Tags.id' => $field2] - ], + ]), 'type' => 'INNER', 'table' => 'articles_tags' ] diff --git a/Cake/Test/TestCase/ORM/Association/BelongsToTest.php b/Cake/Test/TestCase/ORM/Association/BelongsToTest.php index cb1693aea29..7e7ca257041 100644 --- a/Cake/Test/TestCase/ORM/Association/BelongsToTest.php +++ b/Cake/Test/TestCase/ORM/Association/BelongsToTest.php @@ -17,6 +17,7 @@ namespace Cake\Test\TestCase\ORM\Association; use Cake\Database\Expression\IdentifierExpression; +use Cake\Database\Expression\QueryExpression; use Cake\ORM\Association\BelongsTo; use Cake\ORM\Entity; use Cake\ORM\Query; @@ -95,10 +96,10 @@ public function testAttachTo() { $field = new IdentifierExpression('Clients.company_id'); $query->expects($this->once())->method('join')->with([ 'Companies' => [ - 'conditions' => [ + 'conditions' => new QueryExpression([ 'Companies.is_active' => true, ['Companies.id' => $field] - ], + ]), 'table' => 'companies', 'type' => 'LEFT' ] @@ -125,9 +126,9 @@ public function testAttachToConfigOverride() { $association = new BelongsTo('Companies', $config); $query->expects($this->once())->method('join')->with([ 'Companies' => [ - 'conditions' => [ + 'conditions' => new QueryExpression([ 'Companies.is_active' => false - ], + ]), 'type' => 'LEFT', 'table' => 'companies', ] @@ -160,10 +161,10 @@ public function testAttachToNoFields() { $field = new IdentifierExpression('Clients.company_id'); $query->expects($this->once())->method('join')->with([ 'Companies' => [ - 'conditions' => [ + 'conditions' => new QueryExpression([ 'Companies.is_active' => true, ['Companies.id' => $field] - ], + ]), 'type' => 'LEFT', 'table' => 'companies', ] diff --git a/Cake/Test/TestCase/ORM/Association/HasManyTest.php b/Cake/Test/TestCase/ORM/Association/HasManyTest.php index aa27a4c577f..1f903c0d853 100644 --- a/Cake/Test/TestCase/ORM/Association/HasManyTest.php +++ b/Cake/Test/TestCase/ORM/Association/HasManyTest.php @@ -17,6 +17,7 @@ namespace Cake\Test\TestCase\ORM\Association; use Cake\Database\Expression\IdentifierExpression; +use Cake\Database\Expression\QueryExpression; use Cake\ORM\Association\HasMany; use Cake\ORM\Entity; use Cake\ORM\Query; @@ -378,10 +379,10 @@ public function testAttachTo() { $association = new HasMany('Articles', $config); $query->expects($this->once())->method('join')->with([ 'Articles' => [ - 'conditions' => [ + 'conditions' => new QueryExpression([ 'Articles.is_active' => true, ['Authors.id' => $field] - ], + ]), 'type' => 'INNER', 'table' => 'articles' ] @@ -409,9 +410,9 @@ public function testAttachToConfigOverride() { $association = new HasMany('Articles', $config); $query->expects($this->once())->method('join')->with([ 'Articles' => [ - 'conditions' => [ + 'conditions' => new QueryExpression([ 'Articles.is_active' => false - ], + ]), 'type' => 'INNER', 'table' => 'articles' ] @@ -444,10 +445,10 @@ public function testAttachToNoFields() { $association = new HasMany('Articles', $config); $query->expects($this->once())->method('join')->with([ 'Articles' => [ - 'conditions' => [ + 'conditions' => new QueryExpression([ 'Articles.is_active' => true, ['Authors.id' => $field] - ], + ]), 'type' => 'INNER', 'table' => 'articles' ] diff --git a/Cake/Test/TestCase/ORM/Association/HasOneTest.php b/Cake/Test/TestCase/ORM/Association/HasOneTest.php index 7359bea892b..428cc721f7d 100644 --- a/Cake/Test/TestCase/ORM/Association/HasOneTest.php +++ b/Cake/Test/TestCase/ORM/Association/HasOneTest.php @@ -17,6 +17,7 @@ namespace Cake\Test\TestCase\ORM\Association; use Cake\Database\Expression\IdentifierExpression; +use Cake\Database\Expression\QueryExpression; use Cake\ORM\Association\HasOne; use Cake\ORM\Entity; use Cake\ORM\Query; @@ -95,10 +96,10 @@ public function testAttachTo() { $field = new IdentifierExpression('Profiles.user_id'); $query->expects($this->once())->method('join')->with([ 'Profiles' => [ - 'conditions' => [ + 'conditions' => new QueryExpression([ 'Profiles.is_active' => true, ['Users.id' => $field], - ], + ]), 'type' => 'INNER', 'table' => 'profiles' ] @@ -127,9 +128,9 @@ public function testAttachToConfigOverride() { $association = new HasOne('Profiles', $config); $query->expects($this->once())->method('join')->with([ 'Profiles' => [ - 'conditions' => [ + 'conditions' => new QueryExpression([ 'Profiles.is_active' => false - ], + ]), 'type' => 'INNER', 'table' => 'profiles' ] @@ -162,10 +163,10 @@ public function testAttachToNoFields() { $field = new IdentifierExpression('Profiles.user_id'); $query->expects($this->once())->method('join')->with([ 'Profiles' => [ - 'conditions' => [ + 'conditions' => new QueryExpression([ 'Profiles.is_active' => true, ['Users.id' => $field], - ], + ]), 'type' => 'INNER', 'table' => 'profiles' ] diff --git a/Cake/Test/TestCase/ORM/QueryTest.php b/Cake/Test/TestCase/ORM/QueryTest.php index 1e721f7c840..4d23887b503 100644 --- a/Cake/Test/TestCase/ORM/QueryTest.php +++ b/Cake/Test/TestCase/ORM/QueryTest.php @@ -125,9 +125,9 @@ public function testContainToJoinsOneLevel() { ->with(['clients' => [ 'table' => 'clients', 'type' => 'LEFT', - 'conditions' => [ + 'conditions' => new QueryExpression([ ['clients.id' => new IdentifierExpression('foo.client_id')] - ] + ]) ]]) ->will($this->returnValue($query)); @@ -135,9 +135,9 @@ public function testContainToJoinsOneLevel() { ->with(['orders' => [ 'table' => 'orders', 'type' => 'INNER', - 'conditions' => [ + 'conditions' => new QueryExpression([ ['clients.id' => new IdentifierExpression('orders.client_id')] - ] + ]) ]]) ->will($this->returnValue($query)); @@ -145,9 +145,9 @@ public function testContainToJoinsOneLevel() { ->with(['orderTypes' => [ 'table' => 'order_types', 'type' => 'LEFT', - 'conditions' => [ + 'conditions' => new QueryExpression([ ['orderTypes.id' => new IdentifierExpression('orders.order_type_id')] - ] + ]) ]]) ->will($this->returnValue($query)); @@ -155,9 +155,9 @@ public function testContainToJoinsOneLevel() { ->with(['stuff' => [ 'table' => 'things', 'type' => 'INNER', - 'conditions' => [ + 'conditions' => new QueryExpression([ ['orders.id' => new IdentifierExpression('stuff.order_id')] - ] + ]) ]]) ->will($this->returnValue($query)); @@ -165,9 +165,9 @@ public function testContainToJoinsOneLevel() { ->with(['stuffTypes' => [ 'table' => 'stuff_types', 'type' => 'LEFT', - 'conditions' => [ + 'conditions' => new QueryExpression([ ['stuffTypes.id' => new IdentifierExpression('stuff.stuff_type_id')] - ] + ]) ]]) ->will($this->returnValue($query)); @@ -175,9 +175,9 @@ public function testContainToJoinsOneLevel() { ->with(['companies' => [ 'table' => 'organizations', 'type' => 'LEFT', - 'conditions' => [ + 'conditions' => new QueryExpression([ ['companies.id' => new IdentifierExpression('clients.organization_id')] - ] + ]) ]]) ->will($this->returnValue($query)); @@ -185,9 +185,9 @@ public function testContainToJoinsOneLevel() { ->with(['categories' => [ 'table' => 'categories', 'type' => 'LEFT', - 'conditions' => [ + 'conditions' => new QueryExpression([ ['categories.id' => new IdentifierExpression('companies.category_id')] - ] + ]) ]]) ->will($this->returnValue($query));