Filtering results returned by Containable behavior

First of all, let me warn you that what I’m describing here is more or less a hack and is not the most efficient way of handling such things, especially if you are dealing with large volumes of data. It is really here for educational purposes and as food for thought…

Now that I got that out of the way, let’s use our favorite Post HABTM Tag example, and try to do something like this…

We need to get all Posts whose titles start with a ‘t’ and all related Tags, which start with an ‘n’.

This seems like a perfect job for the Containable behavior:

$this->Post->find('all', array(
                                   'conditions'=>array('Post.title LIKE'=>'t%'),
                                   'contain'=>array('Tag'=>array(
                                                              'conditions' => array('Tag.tag LIKE'=>'n%')
                                   ))));

The problem is that we will get back all Posts that start with a ‘t’, but if there is no matching Tag that starts with an ‘n’ we will simply get an empty array.

The returned data might looks something like:

Array
(
    [0] => Array
        (
            [Post] => Array
                (
                    [id] => 4
                    [title] => test
                    [post] =>
                    [created] => 2008-06-20 16:06:53
                    [modified] => 2008-06-20 16:06:53
                    [user_id] => 0
                )

            [Tag] => Array
                (
                )

        )

    [1] => Array
        (
            [Post] => Array
                (
                    [id] => 99
                    [title] => testing 00777
                    [post] =>
                    [created] => 2008-07-18 15:14:24
                    [modified] => 2008-07-18 15:14:24
                    [user_id] => 0
                )

            [Tag] => Array
                (
                    [0] => Array
                        (
                            [id] => 12
                            [tag] => new
                            [status] => 0
                            [PostsTag] => Array
                                (
                                    [id] => 159
                                    [post_id] => 99
                                    [tag_id] => 12
                                    [status] =>
                                )

                        )

                )

        )

Well, the second record looks like what we really need, but we certainly don’t need the first one.

Let’s use our Post model’s afterFind() method to filter out unwanted data:

[sourcecode language=”javascript”]

function afterFind($results, $primary=false) {
if($primary == true) {
foreach($results as $key => $value) {
if(empty($value[‘Tag’])) {
unset($results[$key]);
}
}
}

return $results;
}
[/cc]

Yeah, not the prettiest solution by any means, but it gets the job done relatively painlessly.

Remember that afterFind() will be executed after each find() call, so be mindful of what you are doing, because you may tamper with results that really did not need any filtering.

Related Posts

%d bloggers like this: