Bringing the cold war to CakePHP 1.2 - The Containable Behavior

Posted by Felix Geisendörfer, on May 13, 2007 - in PHP & CakePHP » DataSources, Models & Behaviors

Deprecated post

The authors of this post have marked it as deprecated. This means the information displayed is most likely outdated, inaccurate, boring or a combination of all three.

Policy: We never delete deprecated posts, but they are not listed in our categories or show up in the search anymore.

Comments: You can continue to leave comments on this post, but please consult Google or our search first if you want to get an answer ; ).

Update: JCSiegrist just noticed a problem in the code as I've released it. Somehow I must have deleted an important line of code right before uploading, sry about this. Please get the new version of the code.

Hi folks,

for those of you who feel like it's becoming a regular thing that I'm starting my posts with excuses for not having posted stuff in a while there is good news: I'm one final Math / History exam away from being a free man and done with school. That means I'll start heavy-duty blogging again, publishing all the good and great stuff I've been working on and learning in the past months.

For those of you who can't await this, here comes a little teaser in form of a behavior for Cake 1.2 called 'Containable'. Essentially it is yet another way to unbind associations from a model recursively on the fly. However, I think it's more powerful then all the ones released so far for several reasons:

  • It's a behavior! No manual AppModel hacking here ; ).
  • It is smart: It will recognize when you pass in conflicting associations and automatically correct that.
  • It's for the lazy: Always wondering what $recursive parameter to use? No More! Containable sets it for you.
  • It's convenient: Like to use dot-seperated Model strings as parameters? Arrays of Models? Or both at the same time? No problem!

Alright, enough marketing. Here is the code and how to install it:

  1. Put the code in /app/models/behaviors/containable.php
  2. Create an /app/app_model.php file like this:
php
  1. class AppModel extends Model {
  2.   var $actsAs = array('Containable');
  3. }

Done! Now that was easy. But how to use this new behavior? Well, how would you like to use it? See if one of those alternatives cuts it for you:

php
  1. $this->User->contain(array(
  2.     'UserType',
  3.     'Group' => array(
  4.       'GroupPermission'
  5.     ),
  6.     'Post' => array(
  7.       'Category',
  8.       'Comment' => 'User',     
  9.     )
  10.   )
  11. );

Uhm, that was verbose, how about:

php
  1. $this->User->contain('UserType', 'Group.GroupPermission', 'Post.Category', 'Post.RelatedPost', 'Post.Comment.User');

Or a slightly longer, but more DRY alternative:

php
  1. $this->User->contain('UserType', 'Group.GroupPermission', array(
  2.   'Post' => array('Category', 'RelatedPost', 'Comment.User')
  3. ));

All of the snippets above will cause the exactly the same associations to be present in your next Model::find* query. They are just expressed in a different notation. The function does not care if you use several parameters, strings with dot notation or nested arrays. Define your own containment policies in whatever syntax you like the best! Or mix them : )!

I hope this will become a powerful weapon in your very own war on terrorizing associations you don't want in your resultsets and will make your present doomsday devices obsolete soon ; ).

Oh and before I forget, it might also be worth to read through the code a little. I've experimented with two cool things in there: Namely using static variables as a 'memory' in recursive functions as well as recognizing the root level of the recursive calls by checking if a certain parameter is a boolean : ).

-- Felix Geisendörfer aka the_undefined

PS: Let me know if you find any bugs so I can fix them ASAP.