Sorting with Set::sort() in CakePHP 1.2
You probably already know that CakePHP's Set class is one of the most useful additions to CakePHP 1.2. What may have slipped through the cracks is that you can now sort arrays very easily using a new method - Set::sort().
DISCLAIMER: This feature has only been added to CakePHP 1.2 very recently and AT THE TIME OF WRITING IS ONLY AVAILABLE IN THE SVN BRANCH. There was a major bug in the first edition of the sorting algorithm, which has since been fixed in revision 6573.
Sorting data returned from a database query
You're probably thinking "Err... idiot... just use the ORDER BY functionality in the model!". Yeah, we could do that, but that's not much fun!
Here is my freshly baked People controller, which gracefully passes some data to my view. Nothing special here.
<?php
class PeopleController extends AppController {
var $name = 'People';
function index() {
$this->set('people', $this->Person->find('all'));
}
}
?>
The $people array is in the standard format we receive from our model, and has a belongsTo association (Occupation).
Array
(
[0] => Array
(
[Person] => Array
(
[id] => 1
[occupation_id] => 2
[name] => Harry Potter
[birth_date] => 1980-07-31
)
[Occupation] => Array
(
[id] => 2
[name] => Student
)
)
[1] => Array
(
[Person] => Array
(
[id] => 2
[occupation_id] => 1
[name] => Albus Dumbledore
[birth_date] => 1881-08-01
)
[Occupation] => Array
(
[id] => 1
[name] => Headmaster
)
)
[2] => Array
(
[Person] => Array
(
[id] => 3
[occupation_id] => 3
[name] => Severus Snape
[birth_date] => 1959-01-09
)
[Occupation] => Array
(
[id] => 3
[name] => Professor
)
)
)
Default order
Instead of dumping the entire contents of the $people array after each Set::sort() call, I will present the data in a pretty table.
| Person.id | Person.name | Person.birth_date | Occupation.id | Occupation.name |
|---|---|---|---|---|
| 1 | Harry Potter | 1980-07-31 | 2 | Student |
| 2 | Albus Dumbledore | 1881-08-01 | 1 | Headmaster |
| 3 | Severus Snape | 1959-01-09 | 3 | Professor |
Person.birth_date DESC
Set::sort() currently takes 3 arguments - the array to sort, the array value to sort on, and the sort order. As with the other Set methods, we specify the value to sort on using a key path. This makes it super easy to deal with complex arrays.
$people = Set::sort($people, '{n}.Person.birth_date', 'desc');
| Person.id | Person.name | Person.birth_date | Occupation.id | Occupation.name |
|---|---|---|---|---|
| 1 | Harry Potter | 1980-07-31 | 2 | Student |
| 3 | Severus Snape | 1959-01-09 | 3 | Professor |
| 2 | Albus Dumbledore | 1881-08-01 | 1 | Headmaster |
Occupation.name ASC
It's just as simple sorting on a value of an associated model, in this case the person's occupation. Unfortunately the sort method doesn't current have a default sort order, so we need to specify this. We can also use the PHP constants, SORT_ASC and SORT_DESC.
$people = Set::sort($people, '{n}.Occupation.name', SORT_ASC);
| Person.id | Person.name | Person.birth_date | Occupation.id | Occupation.name |
|---|---|---|---|---|
| 2 | Albus Dumbledore | 1881-08-01 | 1 | Headmaster |
| 3 | Severus Snape | 1959-01-09 | 3 | Professor |
| 1 | Harry Potter | 1980-07-31 | 2 | Student |
What about sorting on an array?
This might seem kinda weird, but lets say you have an array of arrays, and you want to sort based on the highest or lowest value inside the array. Instead of specifying a field to sort upon, we instruct it to sort on the values of an array.
In this example I want to sort the array of people based on the lowest score of the three tests.
$test_scores = array(
array('Adam' => array(7, 6, 4)),
array('Phil' => array(3, 4, 5)),
array('Mick' => array(3, 2, 1)),
);
$test_scores = Set::sort($test_scores, '{n}.{s}.{n}', SORT_ASC);
I have highlighted the lowest score for each person, which is the deciding value in the final sort order.
Array
(
[0] => Array
(
[Mick] => Array
(
[0] => 3
[1] => 2
[2] => 1
)
)
[1] => Array
(
[Phil] => Array
(
[0] => 3
[1] => 4
[2] => 5
)
)
[2] => Array
(
[Adam] => Array
(
[0] => 7
[1] => 6
[2] => 4
)
)
)
Conclusion
Set::sort() is pretty cool, however it is missing a few features I would have liked (such as multisort and a default sort order), however by the time this method makes it into the CakePHP trunk, these features might already be available.
Comments
9 Responses to “Sorting with Set::sort() in CakePHP 1.2”
Leave a Reply
Wow that is really a hell of a good function, Set::sort()!, great article!!, I hope to see more CakePHP and Extjs articles in the future.
Thanks for sharing Adam and the documentation ;)
There are probably tons of goodies that people will have to fish the code to find.
Nice post highlighting this. Motivated me sufficiently to dare to try the svn head branch (6707)
One question: What does the 's' imply in '{n}.{s}.{n}'.
I could really use the "Sorting on an array..." I think - it would allow me to delete a few custom array sorts in controllers
@Aitch: Thanks for your comment. The {s} indicates matching a string key as part of the array path. In the above example, the {s} matches the three names, Adam, Mick and Phil.
Another well written short n' sweet article, thanks ;)
nice
THANK YOU. THIS COMMAND SAVED MY DAY!
This command save my life ... Thanks man