Oneupweb : CakePHP—The Containable Behavior

CakePHP has plenty of tricks up its sleeves. Everyone who has worked with CakePHP has certainly interacted with the ORM once or twice.

The standard way of getting data

//usually done in some controller action
$this->CookieJar->find('first', array(
     'conditions' => array('cookieType' => 'Oreo'),
     'fields => array('capacity','numCookies')
));

This is all good and well for taking a peak at the cookie jar. But what if we want to take a look at the cookies in the cookie jar? We can look deep in the cookie jar with something like this:

//usually done in some controller action
$this->CookieJar->find('first', array(
     'conditions' => array('cookieType' => 'Oreo'),
     'fields => array('capacity','numCookies'),
     'recursive' => 3 //recursive looks into the nested related data
));

The problem here is this can get pretty resource intensive, and it can be difficult to specify all the fields you want from the deeply nested data.

The Containable way

But what if you want to get related data and get it the way you want it? Enter the containable behavior. First an example of setting up your model to use behaviors:

//using it on the class level
<?php
class CookieJar extends AppModel {
    var $name = 'CookieJar';

    var $actsAs = array('Containable');

     //model logic.....
}
//using it on the fly - perhaps some controller some where
$this->CookieJar->Behaviors->attach('Containable');

In both cases you can pass an array of configuration details as the second parameter (as item 2 in the actsAs array, or the second argument to the attach method).
For this example we will be using the default configuration for the Containable behavior.

Let’s take a look at how we might take a sneak peak into the depths of our CookieJar:

$result = $this->CookieJar->find('first',array(
    'fields' => array('name','id'),
    'contain' => array(
        'Cookie' => array(
             'fields' => array('type','isTasty'),
             'conditions' => array('isNotHalfEaten' => 0),
              'Ingredient' => array(
                    'conditions' => array('name' => 'chocolate-chip'),
                    'fields' => array('amount','name')
               )
         )
    )
));

//output the result and we should see something like this
print_r($result);

/*
Array
(
    [CookieJar] => Array
        (
            [Cookie] => Array
                (
                    [0] => Array
                        (
                            [type] => Chocolate Chip
                            [isTasty] => 1
                            [cookie_jar_id] => 1
                            [Ingredient] => Array
                                (
                                    [0] => Array
                                        (
                                            [amount] => 14
                                            [name] => chocolate-chip
                                            [cookie_id] => 3
                                        )

                                )

                        )

                    [1] => Array
                        (
                            [type] => Chocolate Chip Raisin
                            [isTasty] => 0
                            [cookie_jar_id] => 1
                            [Ingredient] => Array
                                (
                                    [0] => Array
                                        (
                                            [amount] => 3
                                            [name] => chocolate-chip
                                            [cookie_id] => 4
                                        )

                                )

                        )

                )

        )

)*/

One more thing to note: anything you could pass to a normal find condition can be passed to a model within contain (sort is handled a little differently, it has to be added to the order clause):

$result = $this->CookieJar->find('first',array(
    'fields' => array('name','id'),
    'contain' => array(
        'Cookie' => array(
             'fields' => array('type','isTasty'),
             'conditions' => array('isNotHalfEaten' => 0),
             'order' => 'Cookie.name DESC',
             'limit' => 2,
              'Ingredient' => array(
                    'conditions' => array('name' => 'chocolate-chip'),
                    'fields' => array('amount','name')
               )
         )
    )
));

Pretty cool stuff! To read more check out the CakePHP.org page on the topic