Adding Sort Order in CakePHP
What will the topic be about?
About how to ensure that it is possible to use CSS or otherwise determine / see in the html output in which order (asc | desc) the column of the table is sorted, and not just which column the table is sorted in.
In short, do it like for example on Yandex Schedules, like this:

How will we do this?
There are several options. At one time, he shoveled a bunch of "cake" sources, and so he did not find a sane (not resource-intensive) solution, except for the following. Of the three solutions:
- Parse html in the overloaded afterRender () function somewhere in your controller (you won’t call it resource-intensive)
- Patch the kernel
- Create your own helper, which is inherited from the PaginatorHelper helper, and overload the method we need there
I chose the third. There are also three reasons for this:
- This will hardly affect performance.
- While I was thinking whether to write an article here or not, the beta version of CakePHP 1.3.0 was released, and there it was already fixed, here is even a ticket with a similar question. But, since this is beta, not all have been updated, and will not be updated soon, because the topic is still relevant
- This does not contradict CakePHP logic.
Implementation
We will add a little. Highly. What was required to achieve from our idea. So.
We find the file \ cake \ libs \ view \ helpers \ paginator.php . We look into it, we find the sort () function. Let's observe the logic:
function sort($title, $key = null, $options = array()) {
$options = array_merge(array('url' => array(), 'model' => null), $options);
$url = $options['url'];
unset($options['url']);
if (empty($key)) {
$key = $title;
$title = __(Inflector::humanize(preg_replace('/_id$/', '', $title)), true);
}
$dir = 'asc';
$sortKey = $this->sortKey($options['model']);
$isSorted = ($sortKey === $key || $sortKey === $this->defaultModel() . '.' . $key);
if ($isSorted && $this->sortDir($options['model']) === 'asc') {
$dir = 'desc';
}
if (is_array($title) && array_key_exists($dir, $title)) {
$title = $title[$dir];
}
$url = array_merge(array('sort' => $key, 'direction' => $dir), $url, array('order' => null));
return $this->link($title, $url, $options);
}
* This source code was highlighted with Source Code Highlighter.
We see that everything you need to add a sort order class is already there. The logic of the authors is incomprehensible, probably they simply did not think or forget.
Now we can add our little codec right in this file, thereby patching the kernel and using the corresponding method number two from my list.
After we
unset($options['url']);
add:// patch:
if ($title == @$this->params['named']['sort']) {
$options['class'] = $this->params['named']['direction'];
}
// endpatch
* This source code was highlighted with Source Code Highlighter.
The advantage of this is that you do not need to carry a separately modified helper with you if you use the same framework for different projects.
Now let's move on to a more interesting method for us.
We create the file \ app \ views \ helpers \ my_paginator.php or with any other name as you wish (just remember to change the class name later). We write in it:
<?php
App::import('Helper','Paginator');
class MyPaginatorHelper extends PaginatorHelper {
/**
* Generates a sorting link
*
* @param string $title Title for the link.
* @param string $key The name of the key that the recordset should be sorted.
* @param array $options Options for sorting link. See #options for list of keys.
* @return string A link sorting default by 'asc'. If the resultset is sorted 'asc' by the specified
* key the returned link will sort by 'desc'.
*/
function sort($title, $key = null, $options = array()) {
$options = array_merge(array('url' => array(), 'model' => null), $options);
$url = $options['url'];
unset($options['url']);
// patch:
if ($title == @$this->params['named']['sort']) {
$options['class'] = $this->params['named']['direction'];
}
// endpath
if (empty($key)) {
$key = $title;
$title = __(Inflector::humanize(preg_replace('/_id$/', '', $title)), true);
}
$dir = 'asc';
$sortKey = $this->sortKey($options['model']);
$isSorted = ($sortKey === $key || $sortKey === $this->defaultModel() . '.' . $key);
if ($isSorted && $this->sortDir($options['model']) === 'asc') {
$dir = 'desc';
}
if (is_array($title) && array_key_exists($dir, $title)) {
$title = $title[$dir];
}
$url = array_merge(array('sort' => $key, 'direction' => $dir), $url, array('order' => null));
return $this->link($title, $url, $options);
}
}
?>
* This source code was highlighted with Source Code Highlighter.
And, in the AppController do not forget to use it:
// ...
class AppController extends Controller {
var $helpers = array('Html', 'Form', 'Ajax', 'Javascript', 'MyPaginator');
// ...
* This source code was highlighted with Source Code Highlighter.
That's all! Your web application will now look something like this:
<a class="asc" id="link1091361951" href="/search/page:1/sort:first_name/direction:desc">First Name</a>
* This source code was highlighted with Source Code Highlighter.
Accordingly, if the class asc | desc is present, then this column is sorted, and how exactly, you can find out by the class itself.
Good luck!
UPD: Given the comments, I want to say that I know about the existence of $ this-> params and that all this information is there. But each time, for each column, writing conditions is not a good idea, especially there are ready-made projects to which you need to add this functionality.