Permalink
Browse files

Paginating now work well with custom find methods.

 _findCount() will now pass $query['operation'] = 'count' for more flexibility.
Custom finders can distinguish the operation and return other $query if needed.
  • Loading branch information...
1 parent 4b3d861 commit bbfaa9e94734bfc7c01bdad0eb80c1d0aa4e14ff @ceeram ceeram committed Feb 29, 2012
Showing with 282 additions and 0 deletions.
  1. +9 −0 lib/Cake/Model/Model.php
  2. +273 −0 lib/Cake/Test/Case/Controller/Component/PaginatorComponentTest.php
@@ -2727,11 +2727,20 @@ protected function _findFirst($state, $query, $results = array()) {
*/
protected function _findCount($state, $query, $results = array()) {
if ($state === 'before') {
+ if (!empty($query['type']) && isset($this->findMethods[$query['type']]) && $query['type'] !== 'count' ) {
@sitedyno
sitedyno Jun 3, 2012 Contributor

Shouldn't this check for $query['operation'] !== 'count' as well to prevent recursion?

@ceeram
ceeram Jun 4, 2012 Member

If query type is count it wont set operation count, this is only needed if custom finders are paginated. The code you link to, is 1.x code which needed this workaround.

+ $query['operation'] = 'count';
+ $query = $this->{'_find' . ucfirst($query['type'])}('before', $query);
+ }
$db = $this->getDataSource();
$query['order'] = false;
if (!method_exists($db, 'calculate') || !method_exists($db, 'expression')) {
return $query;
}
+ if (!empty($query['fields']) && is_array($query['fields'])) {
+ if (!preg_match('/^count/i', current($query['fields']))) {
+ unset($query['fields']);
+ }
+ }
if (empty($query['fields'])) {
$query['fields'] = $db->calculate($this, 'count');
} elseif (is_string($query['fields']) && !preg_match('/count/i', $query['fields'])) {
@@ -229,6 +229,94 @@ class PaginatorAuthor extends CakeTestModel {
}
+/**
+ * PaginatorCustomPost class
+ *
+ * @package Cake.Test.Case.Controller.Component
+ */
+class PaginatorCustomPost extends CakeTestModel {
+/**
+ * useTable property
+ *
+ * @var string
+ */
+ public $useTable = 'posts';
+
+/**
+ * belongsTo property
+ *
+ * @var string
+ */
+ public $belongsTo = array('Author');
+
+/**
+ * findMethods property
+ *
+ * @var array
+ */
+ public $findMethods = array(
+ 'published' => true,
+ 'totals' => true,
+ 'totalsOperation' => true
+ );
+
+/**
+ * _findPublished custom find
+ *
+ * @return array
+ */
+ public function _findPublished($state, $query, $results = array()) {
+ if ($state === 'before') {
+ $query['conditions']['published'] = 'Y';
+ return $query;
+ }
+ return $results;
+ }
+
+/**
+ * _findTotals custom find
+ *
+ * @return array
+ */
+ public function _findTotals($state, $query, $results = array()) {
+ if ($state == 'before') {
+ $query['fields'] = array('author_id');
+ $this->virtualFields['total_posts'] = "COUNT({$this->alias}.id)";
+ $query['fields'][] = 'total_posts';
+ $query['group'] = array($this->alias . '.author_id');
+ return $query;
+ }
+ $this->virtualFields = array();
+ return $results;
+ }
+
+/**
+ * _findTotalsOperation custom find
+ *
+ * @return array
+ */
+ public function _findTotalsOperation($state, $query, $results = array()) {
+ if ($state == 'before') {
+ if (!empty($query['operation']) && $query['operation'] === 'count') {
+ unset($query['limit']);
+ $query['recursive'] = -1;
+ $query['fields'] = array('COUNT(DISTINCT author_id) AS count');
+ return $query;
+ }
+ $query['recursive'] = 0;
+ $query['callbacks'] = 'before';
+ $query['fields'] = array('author_id', 'Author.user');
+ $this->virtualFields['total_posts'] = "COUNT({$this->alias}.id)";
+ $query['fields'][] = 'total_posts';
+ $query['group'] = array('Author.user');
+ return $query;
+ }
+ $this->virtualFields = array();
+ return $results;
+ }
+
+}
+
class PaginatorComponentTest extends CakeTestCase {
/**
@@ -885,4 +973,189 @@ public function testPaginateOrderVirtualFieldSharedWithRealField() {
$this->assertEquals(Set::extract($result, '{n}.PaginatorControllerComment.id'), array(1, 2, 3, 4, 5, 6));
}
+/**
+ * test paginate() and custom find, to make sure the correct count is returned.
+ *
+ * @return void
+ */
+ public function testPaginateCustomFind() {
+ $Controller =& new Controller($this->request);
+ $Controller->uses = array('PaginatorCustomPost');
+ $Controller->constructClasses();
+ $data = array('author_id' => 3, 'title' => 'Fourth Article', 'body' => 'Article Body, unpublished', 'published' => 'N');
+ $Controller->PaginatorCustomPost->create($data);
+ $result = $Controller->PaginatorCustomPost->save();
+ $this->assertTrue(!empty($result));
+
+ $result = $Controller->paginate();
+ $this->assertEquals(Set::extract($result, '{n}.PaginatorCustomPost.id'), array(1, 2, 3, 4));
+
+ $result = $Controller->params['paging']['PaginatorCustomPost'];
+ $this->assertEquals(4, $result['current']);
+ $this->assertEquals(4, $result['count']);
+
+ $Controller->paginate = array('published');
+ $result = $Controller->paginate();
+ $this->assertEquals(Set::extract($result, '{n}.PaginatorCustomPost.id'), array(1, 2, 3));
+
+ $result = $Controller->params['paging']['PaginatorCustomPost'];
+ $this->assertEquals(3, $result['current']);
+ $this->assertEquals(3, $result['count']);
+
+ $Controller->paginate = array('published', 'limit' => 2);
+ $result = $Controller->paginate();
+ $this->assertEquals(Set::extract($result, '{n}.PaginatorCustomPost.id'), array(1, 2));
+
+ $result = $Controller->params['paging']['PaginatorCustomPost'];
+ $this->assertEquals(2, $result['current']);
+ $this->assertEquals(3, $result['count']);
+ $this->assertEquals(2, $result['pageCount']);
+ $this->assertTrue($result['nextPage']);
+ $this->assertFalse($result['prevPage']);
+ }
+/**
+ * test paginate() and custom find with fields array, to make sure the correct count is returned.
+ *
+ * @return void
+ */
+ public function testPaginateCustomFindFieldsArray() {
+ $Controller =& new Controller($this->request);
+ $Controller->uses = array('PaginatorCustomPost');
+ $Controller->constructClasses();
+ $data = array('author_id' => 3, 'title' => 'Fourth Article', 'body' => 'Article Body, unpublished', 'published' => 'N');
+ $Controller->PaginatorCustomPost->create($data);
+ $result = $Controller->PaginatorCustomPost->save();
+ $this->assertTrue(!empty($result));
+
+ $Controller->paginate = array(
+ 'list',
+ 'conditions' => array('PaginatorCustomPost.published' => 'Y'),
+ 'limit' => 2
+ );
+ $result = $Controller->paginate();
+ $expected = array(
+ 1 => 'First Post',
+ 2 => 'Second Post',
+ );
+ $this->assertEquals($expected, $result);
+ $result = $Controller->params['paging']['PaginatorCustomPost'];
+ $this->assertEquals(2, $result['current']);
+ $this->assertEquals(3, $result['count']);
+ $this->assertEquals(2, $result['pageCount']);
+ $this->assertTrue($result['nextPage']);
+ $this->assertFalse($result['prevPage']);
+ }
+
+/**
+ * test paginate() and custom find with fields array, to make sure the correct count is returned.
+ *
+ * @return void
+ */
+ public function testPaginateCustomFindGroupBy() {
+ $Controller =& new Controller($this->request);
+ $Controller->uses = array('PaginatorCustomPost');
+ $Controller->constructClasses();
+ $data = array('author_id' => 2, 'title' => 'Fourth Article', 'body' => 'Article Body, unpublished', 'published' => 'N');
+ $Controller->PaginatorCustomPost->create($data);
+ $result = $Controller->PaginatorCustomPost->save();
+ $this->assertTrue(!empty($result));
+
+ $Controller->paginate = array(
+ 'totals',
+ 'limit' => 2
+ );
+ $result = $Controller->paginate();
+ $expected = array(
+ array(
+ 'PaginatorCustomPost' => array(
+ 'author_id' => '1',
+ 'total_posts' => '2'
+ )
+ ),
+ array(
+ 'PaginatorCustomPost' => array(
+ 'author_id' => '2',
+ 'total_posts' => '1'
+ )
+ )
+ );
+ $this->assertEquals($expected, $result);
+ $result = $Controller->params['paging']['PaginatorCustomPost'];
+ $this->assertEquals(2, $result['current']);
+ $this->assertEquals(3, $result['count']);
+ $this->assertEquals(2, $result['pageCount']);
+ $this->assertTrue($result['nextPage']);
+ $this->assertFalse($result['prevPage']);
+
+ $Controller->paginate = array(
+ 'totals',
+ 'limit' => 2,
+ 'page' => 2
+ );
+ $result = $Controller->paginate();
+ $expected = array(
+ array(
+ 'PaginatorCustomPost' => array(
+ 'author_id' => '3',
+ 'total_posts' => '1'
+ )
+ ),
+ );
+ $this->assertEquals($expected, $result);
+ $result = $Controller->params['paging']['PaginatorCustomPost'];
+ $this->assertEquals(1, $result['current']);
+ $this->assertEquals(3, $result['count']);
+ $this->assertEquals(2, $result['pageCount']);
+ $this->assertFalse($result['nextPage']);
+ $this->assertTrue($result['prevPage']);
+ }
+
+/**
+ * test paginate() and custom find with returning otehr query on count operation,
+ * to make sure the correct count is returned.
+ *
+ * @return void
+ */
+ public function testPaginateCustomFindCount() {
+ $Controller =& new Controller($this->request);
+ $Controller->uses = array('PaginatorCustomPost');
+ $Controller->constructClasses();
+ $data = array('author_id' => 2, 'title' => 'Fourth Article', 'body' => 'Article Body, unpublished', 'published' => 'N');
+ $Controller->PaginatorCustomPost->create($data);
+ $result = $Controller->PaginatorCustomPost->save();
+ $this->assertTrue(!empty($result));
+
+ $Controller->paginate = array(
+ 'totalsOperation',
+ 'limit' => 2
+ );
+ $result = $Controller->paginate();
+ $expected = array(
+ array(
+ 'PaginatorCustomPost' => array(
+ 'author_id' => '3',
+ 'total_posts' => '1'
+ ),
+ 'Author' => array(
+ 'user' => 'larry',
+ )
+ ),
+ array(
+ 'PaginatorCustomPost' => array(
+ 'author_id' => '1',
+ 'total_posts' => '2'
+ ),
+ 'Author' => array(
+ 'user' => 'mariano'
+ )
+ )
+ );
+ $this->assertEquals($expected, $result);
+ $result = $Controller->params['paging']['PaginatorCustomPost'];
+ $this->assertEquals(2, $result['current']);
+ $this->assertEquals(3, $result['count']);
+ $this->assertEquals(2, $result['pageCount']);
+ $this->assertTrue($result['nextPage']);
+ $this->assertFalse($result['prevPage']);
+ }
}

0 comments on commit bbfaa9e

Please sign in to comment.