gold stars
| Bill Hunt |
|
Pagination, etc.
So the big noteworthy development of the week is pagination. With the exception of a couple additional methods and enhancements, pagination in both the controller and the view is basically complete.
Here's a basic rundown of how it works:
// controllers/posts_controller.php: //
class PostsController extends AppController {
var $components = array('RequestHandler');
var $paginate = array('limit' => 3, 'order' => array('Post.created' => 'desc'));
function index() {
// Fetches paged results; this is equivalent to calling findAll.
$data = $this->paginate();
$this->set(compact('data'));
}
}
// views/layouts/default.ctp: //
<?=$html->docType('xhtml-strict'); ?>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title><?=$title_for_layout?></title>
<?=$html->charset('UTF-8'); ?>
<?=$html->css('default'); ?>
<?=$javascript->link(array('prototype', 'event-selectors')); ?>
<style type="text/css">
div.disabled {
display: inline;
float: none;
clear: none;
color: #C0C0C0;
}
</style>
</head>
<body>
<div id="main">
<div id="spinner" style="display: none; float: right;">
<?=$html->image('spinner.gif'); ?>
</div>
<div id="content">
<?=$content_for_layout; ?>
</div>
</div>
</body>
</html>
// webroot/img/spinner.gif: //
// views/posts/index.ctp: //
<? $paginator->options(array('update' => 'content', 'indicator' => 'spinner')); ?>
<?=$paginator->prev('<< Previous', null, null, array('class' => 'disabled')); ?> |
<?=$paginator->next('Next >>', null, null, array('class' => 'disabled')); ?> |
Sort by
<?=$paginator->sort('created'); ?> |
<?=$paginator->sort('updated'); ?> |
Page <?=$paginator->counter(); ?>
<? pr($data); ?>
And with that we end up with a basic pagination display, with the record data dumped below it:

So there you have it: CakePHP pagination with Ajax.
Overview
-
The Controller - In the controller, we start be defining the pagination defaults in the
$paginatevariable. It is important to note here that the'order'key must be defined in the array structure given. Then, in the action, we call thepaginate()method, which returns pagedfindAll()results from the model, and grabs some additional paging statistics, which are passed to the View behind the scenes. This method also addsPaginatorHelperto the list of helpers in your controller, if it has not been added already. -
The Layout - A couple of things to pay attention to here: we're including the Prototype library in the header, we're setting up our status indicator image,
spinner.gif, with a wrapper DIV called "spinner", and we're setting up our main content wrapper DIV, "content". -
'indicator' - The
'indicator'key (see the first line of theindex.ctpview code) is actually a new feature inAjaxHelper, which you can include in any Ajax generator (links, buttons, etc). It allows you to specify a DOM ID of an element that will be shown while an Ajax request is loading, and hidden when it completes, which saves you from having to write an extra bit of JavaScript. -
The View - We start by calling
$paginator->options(), which defines a default set of options which will be added to all pagination links. In this case we're saying that for all pagination links, we want to update the element with the ID 'content' with the resulting data, and we want to show 'spinner' as the loading indicator. This brings up an important point: by specifying the'update'key,PaginatorHelperknows that we want to generate Ajax links. If this key is not specified, we'll get plain old standard-issue non-Ajax links.
The next lines are defining the 'previous' and 'next' links. The array parameters to each of those methods are the options that apply if the links are disabled, i.e. if there is no previous or next page, respectively. By default, these methods will render nothing if the corresponding page does not exist, but by passing them a set of options to use when disabled, they will render their link text (or alternate text, as is optionally specified in the 3rd parameter) in a wrapper DIV, with the given options.
The following two method calls define sorting links, so users can choose how to sort the paged results. The final method,counter()generates a simple page counter so we can keep track of what page we're on, and how many pages there are in total.
The output of all these methods is highly configurable, and clearly this is a very simple overview. However, do not despair: documentation on all this and more will be forthcoming very shortly.
Side Note
"Well, okay, you're not privy to all the new shit..."The Big Lebowski
What is it that makes newcomers to this community and others outside it come in and think they know more about the core code or what to do with it than we do? (By "we" I mean the CakePHP team, not the royal we).
Don't get me wrong, I'm all for new ideas, but it never ceases to amaze me how people think that they have better ideas on how certain things should work, or that we somehow "need" their ideas. More often than not, these people have little to no knowledge or understanding of the philosophies on which Cake is built.
News flash: we've been around for a couple of years now, and in that time we've grown to become the biggest and most popular PHP framework available (by a long shot, depending on which figures you follow), and we did so by keeping our own counsel on decisions related to code and functionality. If you have new ideas, great. By all means, let us hear them; but maintaining a quality framework is all about being picky. If you think you're right and we think you're wrong, chances are, you're wrong.
Now, dear readers, please understand, and take the preceding thoughts in the spirit which they are meant. The fact is, they are not intended for most of you, but those they are intended for, you know who you are.
27 comments
However, I think one shouldn't use the short tags syntax in public/educational posts (the one that echo's it's contents directly) because not all hosts support it which could lead to portability issues.
I guess it's possible to tweak the code in the controller more to be able to paginate a model, that isn't the default one ( included in uses or loadModel()..), I suppose there is support for paginating multiple models too, I'll dig in that more
Othman: You can paginate any model loaded in the controller with $uses. You can also define pagination defaults on a per-model basis, and instructions on how to do these things are all forthcoming in the documentation.
However, after reading the last couple of paragraphs, including the bit saying "take the preceding thoughts in the spirit which they are meant", I can't help but feel very disappointed in your writing.
Yes it's "your" framework and hence you are free to do what you wish with it. It's just a shame you sound so, well, big-headed about it. You have a public issue tracker (Trac), which accepts enhancement requests, so either restrict access or don't complain. Yes it's a pain when people keep reopening tickets, but they're probably not the ones reading your blog. It's just something you have to learn to live with.
Meh, anyway, regardless of this text, if I find something I feel needs changing and it's not been mentioned, I'll still create a ticket and send a patch if possible.
Great posts lately, thanks a lot. Could you maybe write about the i18n and the L10n functionality in cake? I mean a bit more info that just "use __('text')". Like how db info works with it and stuff? Also how to make the .mo and .po files and such stuff.
Thanks again.
For me, it's important for other people to understand why we code the way we code, but when people are willing to sacrifice a vision they don't see or understand for their own narrow gain, this is not only self-centered, but ignorant.
As a side note it is probably (maybe) a good idea to eventually remove much of the "snippets" on CakeForge made for older Cake versions. Or at least the stuff that has made it into core to prevent confusion :-) Had someone asking me about his pagination problems and had no idea he was using something very old.
As Mladen (comment 6) said afore, a i18n article would be great...
Dr. Sani & Oliver: I'll try and get some info out on that stuff as soon as I can, however, i18n is still under heavy development, and some aspects of the features have not yet been completed, i.e. db-based translations.
1)
$paginator->options(array('update' => 'content', 'indicator' => 'spinner'));
There is no such function in Paginator nor parent classes.
2)
$paginator->counter();
gives an error, page not defined..
using the version 1.2 in the trunk
However, with this pagination, I was kinda wondering if it would be possible to pull out the page-number, and the total number of pages, in individual functions? I'm asking because I sometimes code pages in a different language (specifically Danish), and the "1 of 4" looks kinda silly when the rest is in another language. I've so far just used some string replacing hacking to avoid editing the Paginator class itself, but if there's another option I haven't seen, or if it's perhaps coming with the i18n part, then please feel free to post it if you get the time.
Anyway, good work on v1.2 so far! It really rocks, and it's going to be ever so cool when it gets done. :)
PaginatorHelper also includes methods which allow you to query page and count values directly.
Thanks! I had actually not even thought to look in the PaginatorHelper source, which is kinda odd, because I did that with some of the other things, though there are some things I still find kinda confusing about the v1.2 release, possibly because I don't get the idea behind them yet. One of those is the model behaviors, but I guess more information about those will come at a later time.
Oh, and looking at the source for the PaginatorHelper, I find myself a bit confused about the range format, but I think I'm just going to fiddle with it, and try it out to see what it looks like. :)
One thing I've found is that when setting conditions in the controller using $this->paginate($conditions), the paginator "forgets" its conditions when sorting or changing pages. i.e. The first page is displayed filtered correctly, but when clicking Next, Prev or sorting, it loses all the conditions.
I know I can hardcode conditions into the $paginate property, but I need to set them dynamically through the controller action.
Any thoughts?
In the meantime, there are several approaches to dealing with conditions, the quickest and easiest of which would be to just store them in the session. Another way would be to pass them as named arguments into the paginator URLs you generate, like so:
$paginator->next("Next >>", array("url" => array("q" => $searchString)));
Or, pass it in the query string:
array("url" => array('?' => "q=" . urlencode($searchString)))
(I know, I know, the syntax for that last one was ugly. I'm working on it).
class MyController extends AppController
{
var $name = 'My';
var $uses = array('Model');
var $paginate = array();
function index()
{
$this->paginate['Model'] = array(
'limit' => 2,
'order' => array ('Model.name' => 'asc')
);
$data = $this->paginate('Model');
}
}
?>
I hit a couple of hitches and had a flick through the code to see if it was user-solvable. seems like no, so looking for input
1) A string constraint produces sql with "Array" as the constraint
2) It's not possible to specify the fields array
I don't know if there are greater plans afoot, but both of these are necessasry to be able to paginate a result set that include any aggregate functions (afaik)
As I don't know if this is by design I didn't put it in a ticket.
Comments?
Cheers,
AD
Andy: you should be able to specify a 'fields' key in the $paginate property, and assign it a value just like you would pass to the $fields parameter in findAll. As for your other issue, try wrapping the conditions string in an array.
Cheers
Can you help my please with this :
Call to undefined function paginate()
I have cakephp1.2.0.4451alpha.
thaks
Or here: irc.freenode.net #cakephp
read more comments, then add one of your own.

