Monday, November 29, 2010

Sorting Related HABTM Model Data With Cake's Containable Behavior

Quite some time ago, Felix Geisendörfer created a behavior for CakePHP called `Containable`, which has since been added to CakePHP's core. I read about it and it always looked pretty interesting, but I dropped out of the CakePHP realm for a few years while I was busy completing my CSE degree. But now I'm back, and I stumbled upon a need to sort related data from a HABTM relationship. My heart filled with dread as I contemplated the ways to do this, mostly with CakePHP trickery and slow performing SQL queries. But luckily, Containable came to the rescue! I found a blog post that was extremely helpful, but it was written for the pagination helper, which I wasn't using. So below is the solution rewritten for regular find() calls to a model. Here's what my association looks like:
Event hasAndBelongsToMany Image

And I needed to make it so that when retrieving an event, the associated Image data was sorted by a specific field. Here's how to do that using Containable:
[php]public function myAction($order) {
$this->Event->Behaviors->attach('Containable');
$this->Event->contain(array(
'Image' => array(
'order' => $order
)
));

$event = $this->Event->find('first');

$this->set(compact('event'));
}[/php]

Well that was easy... hopefully I just saved you from a world of hurt.

Sunday, November 28, 2010

PHP Ternary Reference Assignment

I'm a fan of CakePHP, but some times it can do some goofy things. One such example is the afterFind() Model callback, whose $results parameter may contain data in two different formats depending on how the find results were retrieved elsewhere in the code. This breaks the blackbox concept of functions in which the function shouldn't care less of what happens outside it's scope. Because of this oddity, you have to support either the model data existing in the $results array under a key named after the model, or the data existing directly in the $results array as simple key/value pairs. I attempted to make this easy to deal with by doing the following:

[php]$res = ($primary)
? &$result['MyModel']
: &$result;[/php]

But for whatever reason, PHP fails with an "unexpected &" error after the ternary `?` operator. The ternary syntax is just a shorthand I use for a really easy if-then, so I decided to expand it and see what happens.

[php]if($primary) {
$res = &$result['MyModel'];
} else {
$res = &$result;
}[/php]

This code runs without a hitch. So then what's happening? Your guess is as good as mine. I honestly can't discern what's making PHP break on the ternary version. Although the ternary syntax is rather simple and can be considered synonymous to the if-then code, PHP seems to handle them differently and consequently breaks reference assignment when using ternary.