From 58c1f5238de3805cd8488e8d8c429c0ce2bf55dc Mon Sep 17 00:00:00 2001 From: Patrick Conroy Date: Fri, 5 Sep 2014 11:08:57 -0400 Subject: [PATCH] Allow associations inline with contain specify their own finder methods --- src/ORM/Association.php | 5 +-- .../SelectableAssociationTrait.php | 2 +- src/ORM/EagerLoader.php | 3 +- src/ORM/Table.php | 31 +++++++++++++++++ .../Component/PaginatorComponentTest.php | 5 ++- tests/TestCase/ORM/QueryTest.php | 33 +++++++++++++++++++ .../TestApp/Model/Table/AuthorsTable.php | 8 +++++ 7 files changed, 80 insertions(+), 7 deletions(-) diff --git a/src/ORM/Association.php b/src/ORM/Association.php index 7a5e36ed335..f67fc5696cf 100644 --- a/src/ORM/Association.php +++ b/src/ORM/Association.php @@ -445,7 +445,8 @@ public function attachTo(Query $query, array $options = []) { 'conditions' => [], 'fields' => [], 'type' => empty($options['matching']) ? $this->joinType() : 'INNER', - 'table' => $target->table() + 'table' => $target->table(), + 'finder' => null ]; if (!empty($options['foreignKey'])) { @@ -455,7 +456,7 @@ public function attachTo(Query $query, array $options = []) { } } - $dummy = $this->find()->eagerLoaded(true); + $dummy = $this->find($options['finder'])->eagerLoaded(true); if (!empty($options['queryBuilder'])) { $dummy = $options['queryBuilder']($dummy); if (!($dummy instanceof Query)) { diff --git a/src/ORM/Association/SelectableAssociationTrait.php b/src/ORM/Association/SelectableAssociationTrait.php index b7712ef947f..7cefca4212a 100644 --- a/src/ORM/Association/SelectableAssociationTrait.php +++ b/src/ORM/Association/SelectableAssociationTrait.php @@ -86,7 +86,7 @@ protected function _buildQuery($options) { } $fetchQuery = $this - ->find('all') + ->find(isset($options['finder']) ? $options['finder'] : 'all') ->where($options['conditions']) ->eagerLoaded(true) ->hydrate($options['query']->hydrate()); diff --git a/src/ORM/EagerLoader.php b/src/ORM/EagerLoader.php index 07408b300cf..88f40a6a248 100644 --- a/src/ORM/EagerLoader.php +++ b/src/ORM/EagerLoader.php @@ -57,7 +57,8 @@ class EagerLoader { 'fields' => 1, 'sort' => 1, 'matching' => 1, - 'queryBuilder' => 1 + 'queryBuilder' => 1, + 'finder' => 1 ]; /** diff --git a/src/ORM/Table.php b/src/ORM/Table.php index f983de98d1e..1db50a61a7c 100644 --- a/src/ORM/Table.php +++ b/src/ORM/Table.php @@ -1440,6 +1440,9 @@ protected function _processDelete($entity, $options) { * @throws \BadMethodCallException */ public function callFinder($type, Query $query, array $options = []) { + if (is_array($type)) { + list($type, $options) = $this->_inferFinderTypeAndOptions($type, $options); + } $query->applyOptions($options); $options = $query->getOptions(); $finder = 'find' . ucfirst($type); @@ -1934,4 +1937,32 @@ public function __debugInfo() { ]; } +/** + * Helper method to infer the requested finder and its options. + * + * Returns the inferred options from the finder $type. + * + * @return array + */ + protected function _inferFinderTypeAndOptions(array $type) { + $options = []; + if (count($type)) { + $v = array_values($type)[0]; + $k = array_keys($type)[0]; + if (is_array($v)) { + $type = $k; + $options = $v; + } elseif (is_string($v) && !$k) { + $type = $v; + } elseif (is_string($v)) { + $type = $k; + $options = [$v]; + } + } + return [ + $type, + $options + ]; + } + } diff --git a/tests/TestCase/Controller/Component/PaginatorComponentTest.php b/tests/TestCase/Controller/Component/PaginatorComponentTest.php index 8c91dfc6983..80528992ef0 100644 --- a/tests/TestCase/Controller/Component/PaginatorComponentTest.php +++ b/tests/TestCase/Controller/Component/PaginatorComponentTest.php @@ -150,13 +150,12 @@ public function testPaginateCustomFinderOptions() { $this->loadFixtures('Post'); $settings = [ 'PaginatorPosts' => [ - 'finder' => 'author', - 'author_id' => 1 + 'finder' => ['author' => ['author_id' => 1]] ] ]; $table = TableRegistry::get('PaginatorPosts'); - $expected = $table->find('author', ['conditions' => ['PaginatorPosts.author_id' => $settings['PaginatorPosts']['author_id']]]) + $expected = $table->find('author', ['conditions' => ['PaginatorPosts.author_id' => $settings['PaginatorPosts']['finder']['author']['author_id']]]) ->count(); $result = $this->Paginator->paginate($table, $settings)->count(); diff --git a/tests/TestCase/ORM/QueryTest.php b/tests/TestCase/ORM/QueryTest.php index 8db09f90aa5..2878a487491 100644 --- a/tests/TestCase/ORM/QueryTest.php +++ b/tests/TestCase/ORM/QueryTest.php @@ -2111,4 +2111,37 @@ public function testCleanCopy() { $this->assertNull($copy->clause('order')); } +/** + * Test that finder options sent through via contain are sent to custom finder. + * + * @return void + */ + public function testContainFinderCanSpecifyOptions() { + $table = TableRegistry::get('Articles'); + $table->belongsTo( + 'Authors', + ['className' => 'TestApp\Model\Table\AuthorsTable'] + ); + $authorId = 1; + + $resultWithoutAuthor = $table->find('all') + ->where(['Articles.author_id' => $authorId]) + ->contain([ + 'Authors' => [ + 'finder' => ['byAuthor' => ['author_id' => 2]] + ] + ]); + + $resultWithAuthor = $table->find('all') + ->where(['Articles.author_id' => $authorId]) + ->contain([ + 'Authors' => [ + 'finder' => ['byAuthor' => ['author_id' => $authorId]] + ] + ]); + + $this->assertEmpty($resultWithoutAuthor->first()['author']); + $this->assertEquals($authorId, $resultWithAuthor->first()['author']['id']); + } + } diff --git a/tests/test_app/TestApp/Model/Table/AuthorsTable.php b/tests/test_app/TestApp/Model/Table/AuthorsTable.php index 7528f51eaf7..b83da8bf175 100644 --- a/tests/test_app/TestApp/Model/Table/AuthorsTable.php +++ b/tests/test_app/TestApp/Model/Table/AuthorsTable.php @@ -12,6 +12,7 @@ namespace TestApp\Model\Table; use Cake\ORM\Table; +use Cake\ORM\Query; /** * Author table class @@ -23,4 +24,11 @@ public function initialize(array $config) { $this->hasMany('articles'); } + public function findByAuthor(Query $query, array $options = []) { + if (isset($options['author_id'])) { + $query->where(['Articles.id' => $options['author_id']]); + } + return $query; + } + }