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...
ceeram committed Feb 29, 2012
1 parent 4b3d861 commit bbfaa9e94734bfc7c01bdad0eb80c1d0aa4e14ff
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
View
@@ -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' ) {

This comment has been minimized.

Show comment Hide comment
@sitedyno

sitedyno Jun 3, 2012

Contributor

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

@sitedyno

sitedyno Jun 3, 2012

Contributor

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

This comment has been minimized.

Show comment Hide comment
@sitedyno

sitedyno Jun 3, 2012

Contributor

This comment has been minimized.

Show comment Hide comment
@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.

@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.