Schneimi’s Dev Weblog


Behaviors on model associations

Posted in CakePHP by schneimi on September 6, 2009
Tags: , , , ,

I made a simple behavior that serializes (beforeSave) and deserializes (afterFind) data to and from the database.

The behavior works fine as long as I do a find directly over the model where my behavior is attached, but if I do a find through associated models using the containable behavior, my behavior is not triggered at all and I have to deserialize the data manually after the find, which is very uncomfortable.

Model behavior afterFind is triggered

$this->Model->find(...);

Model behavior afterFind isn’t triggered

$this->OtherModel->contain('Model');
$this->OtherModel->find(...);

I found a ticket and a thread concerning this topic, and it turns out that this is an existing problem with CakePHP for a longer time already.

Because I use the latest CakePHP 1.2.5 and couldn’t find any working patch for it, I adapted the patch found in the thread to this version. I am not very familiar with the core, so it’s still quick&dirty and I cannot tell if it works for every setup. Anyway I hope this still helps other people that run into the same problem.

Here are the changes that have to be done in the cake\libs\model\datasources\dbo_source.php, just place the code after the lines:

Line 877: $resultSet[$i][$association] = $linkModel->afterFind($resultSet[$i][$association]);

foreach ($linkModel->Behaviors->attached() as $behavior) {
  if ($behavior != 'Containable') {
    $data = array(array($association => $resultSet[$i][$association]));
    $filtered_data = $linkModel->Behaviors->{$behavior}->afterFind($linkModel, $data, false);
    
    if ($filtered_data) {
      $resultSet[$i][$association] = $filtered_data[0][$association];
    }
  }
}

Line 741: function queryAssociation(&$model, &$linkModel, $type, $association, $assocData, &$queryData, $external = false, &$resultSet, $recursive, $stack) {

foreach ($linkModel->Behaviors->attached() as $behavior) {
  if ($behavior != 'Containable') {
    $return = $linkModel->Behaviors->{$behavior}->beforeFind($linkModel, $assocData);
    $assocData = (is_array($return)) ? $return : $assocData;
  }
}

Line 716: $data = $model->{$className}->afterFind(array(array($className => $results[$i][$className])), false);

foreach ($model->{$className}->Behaviors->attached() as $behavior) {
  if ($behavior != 'Containable') {
    $filtered_data = $model->{$className}->Behaviors->{$behavior}->afterFind($model->{$className}, $data, false);
    
    if ($filtered_data) {
      $data = $filtered_data;
    }
  }
}

edit:
I added support for the beforeFind callback, because I needed this on a softdelete behavior to filter associated deleted data as well.

I also found out that you don’t have to really hack the core, instead just copy the cake\libs\model\datasources\dbo_source.php into your app app\models\datasources\dbo_source.php and change this file instead.

Advertisements

3 Responses to 'Behaviors on model associations'

Subscribe to comments with RSS or TrackBack to 'Behaviors on model associations'.

  1. Lapinski said,

    by changing the core you basically say goodbye to the whole cakephp community. I think this is really the last thing you wanna do…

    • schneimi said,

      If you read until the end, the core isn’t changed, just overloaded for my needs…

      Anyway I don’t like this either, but I didn’t find any other way to overcome the problem. So what I do, is saying hi to the whole community, you are all very welcome to give a better solution.

      So far I keep following the motto, what helps me could help others.


  2. We are using Cake 1.3.x but have transitioned to Cake 2.x for our newer apps. This was a problem in Cake 1.3.x and completely skipped 2.x. Apparently it is fixed in Cake 3.x. The dbo_source.php did not change much from 1.2.x to 1.3.x with some adaptation I have made the same changes to ensure our callbacks (namely beforeFind and afterFind) are called as expected. Nice work!


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s


%d bloggers like this: