From The Blog

Easy sorting with CakePHP

3 November 2011, in CakePHP

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().

Note: 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.



Post a Comment