With changeset 6918 a new behavior has been introduced to CakePHP: Containable. It’s a mix of the original Containable behavior by Felix Geisendörfer (aka the_undefined) and the Bindable behavior by Mariano Iglesias.

To use the new behavior, you either have to add it to the $actsAs property of your model:

class Post extends AppModel {
    var $actsAs = array('Containable');
}

or you can attach the behavior on the fly with:

$this->Post->Behaviors->attach('Containable');

Ok, now let’s have a look at some examples. For the examples I will use the three models Post, Comment, and Tag, with the associations: Post hasMany Comments, Post hasAndBelongsToMany Tags.

If we perform a simple find all statement, then we get back all posts plus all associated records, something like:

debug($this->Post->find('all'));

[0] => Array
        (
            [Post] => Array
                (
                    [id] => 1
                    [title] => First article
                    [content] => aaa
                    [created] => 2008-05-18 00:00:00
                )
            [Comment] => Array
                (
                    [0] => Array
                        (
                            [id] => 1
                            [post_id] => 1
                            [author] => Daniel
                            [email] => dan@example.com
                            [website] => http://example.com
                            [comment] => First comment
                            [created] => 2008-05-18 00:00:00
                        )
                    [1] => Array
                        (
                            [id] => 2
                            [post_id] => 1
                            [author] => Sam
                            [email] => sam@example.net
                            [website] => http://example.net
                            [comment] => Second comment
                            [created] => 2008-05-18 00:00:00
                        )
                )
            [Tag] => Array
                (
                    [0] => Array
                        (
                            [id] => 1
                            [name] => A
                        )
                    [1] => Array
                        (
                            [id] => 2
                            [name] => B
                        )
                )
        )

But often you don’t want to get all those data, and that’s when the Containable behavior comes to the rescue.

To get only the posts, you can do the following:

$this->Post->contain();
debug($this->Post->find('all'));

or

debug($this->Post->find('all', array('contain' => false)));

or without the Containable behavior

$this->Post->recursive = -1;
debug($this->Post->find('all'));

With the contain() method resp. the “contain” option we specify from which of the associated models we want to get data. If we want to get all posts plus the associated tags (without the comments), we would do it in the following way:

$this->Post->contain('Tag'); // we could also use an array
debug($this->Post->find('all'));

or

debug($this->Post->find('all', array('contain' => 'Tag'))); // we could also use an array

or without the Containable behavior

$this->Post->unbindModel(array('hasMany' => array('Comment')));
debug($this->Post->find('all'));

As you can see, the code using the Containable behavior is much cleaner than the code without it.

But that’s not all the Containable behavior can do for you. You can also filter the data of the associated models. If you are interested in the posts and the names of the comment authors, you could write:

$this->Post->contain('Comment.author');
debug($this->Post->find('all'));

or

debug($this->Post->find('all', array('contain' => 'Comment.author')));

[0] => Array
        (
            [Post] => Array
                (
                    [id] => 1
                    [title] => First article
                    [content] => aaa
                    [created] => 2008-05-18 00:00:00
                )
            [Comment] => Array
                (
                    [0] => Array
                        (
                            [author] => Daniel
                            [post_id] => 1
                        )
                    [1] => Array
                        (
                            [author] => Sam
                            [post_id] => 1
                        )
                )
        )

As you can see, the comment arrays only contain the author plus the post_id (which is needed by Cake to map the results).

You can also filter the (comment) data by using a condition:

$this->Post->contain('Comment.author = "Daniel"');
debug($this->Post->find('all'));

or 

debug($this->Post->find('all', array('contain' => 'Comment.author = "Daniel"')));

[0] => Array
        (
            [Post] => Array
                (
                    [id] => 1
                    [title] => First article
                    [content] => aaa
                    [created] => 2008-05-18 00:00:00
                )
            [Comment] => Array
                (
                    [0] => Array
                        (
                            [id] => 1
                            [post_id] => 1
                            [author] => Daniel
                            [email] => dan@example.com
                            [website] => http://example.com
                            [comment] => First comment
                            [created] => 2008-05-18 00:00:00
                        )
                )
        )

As you can see from these examples, the Containable behavior is very powerful, and I recommend to have a look at the tests.

Anyway, it’s very cool to have this behavior in the core :)