Sharing technology, ideas, insights!
Call: +91 710 466 0336         Email: hello@sanisoft.com

Blog

Attach & detach behaviors at run-time in CakePHP Models

Update Cakebaker blog tells us that attaching and detaching behaviors on the fly is now built in CakePHP core (we obviously have been doing something right)

Behaviors are one of the best things that have been added to CakePHP 1.2, they allow you to add functionality to your models in a very elegant and modular fashion. They also promote a lot of code reuse.

Perhaps a real life example would illustrate it better. In Cheesecake Photoblog when a new photo was added it was the controller which checked if a proper file was uploaded, resized it and then move it, once this was done the EXIF vendor class was used to extract the EXIF info and store that in the database. The code for the photos/add method was fairly bloated and ugly. However with behaviors, in Cheesecake Photoblog 2.0, we were able to move these two task away from the action and now all that the add action code does is call $this->Photo->save() the attached ‘file’ and ‘exif’ behaviors automagically take care of the rest. Also with the file upload handling moved code to a behavior it can be reused with any model which needs to handle file uploads

While writing code for V2.0 I needed to detach some behaviors at run-time. More specifically I did not want the File Upload and EXIF behavior to act during an edit action when the picture was not being replaced! This functionality was not built in to CakePHP 1.2 last I checked and I wanted it NOW – solution? code it!

Detaching behavior was simple in the controller I did

This got me thinking… why not have a method which will do it more elegantly. The result was

If you want to use the method in your controller – do

or

The benefit of being allowed to code on Open Source projects during work is that you can do what you want and as you want. While just detaching behaviors at runtime would be enough for most projects, For safety I wanted a method to attach a behavior at runtime.

A bit of delving in to cake’s core model’s constructor code (related to behavior attachment) I ended up with following code

You can use above method in controller like same as you define your actsAs array

Of Course – you all know that the above two methods should go into your app_model.php – right?

P.S. Thanks to Dr. Tarique Sani for his help with this article

About the Author

Amit Badkas is Zend certified PHP5 and Zend Framework engineer, and has been working in SANIsoft for past 10 years, his present designation is 'Technical Manager'

10 comments

  1. Pingback: PHPDeveloper.org

  2. Pingback: developercast.com » Sanisoft.com: Attach & detach behaviors at run-time in CakePHP Models

  3. Interesting…
    I had checked cake 3 years ago, but didn’t used it.

    I’m more of a “self doer” than to rely on framework, but the idea of relying to behaviors attached to elements surely looks nice.

    I’ll try to put some time on cake in the next week, I could be agreeably surprised.

    Thanks for your article.

  4. Great code. Thanks! :) But there are several problems.
    1) there is no loadBehavior in version 1.2 so I had to change ir to App::import('Behavior', $behavior);
    2) It doesn’t work at all. To remove all (hopefully) behaviours you have to loop through attachments like this foreach ($behaviors as $behavior) // Loop through passed behaviors { unset($this->actsAs); foreach ($this->Behaviors->_attached as $key => $attached) { if ($attached == $behavior) unset($this->Behaviors->_attached[$key]); } unset($this->Behaviors->$behavior); unset($this->locale); }
    3) cakephp caches queries. You can’t attach and detach behaviours in the next line. I’m now trying to figure out how to rebuild model shema.

  5. I found another solution. For example I have a model with a variable var $actsAs   = array('i18n' => array('fields'=>array('name')) );
    I can detach that beavior any time and attach back again. I’ve written 4 functions to do that. 1) private function to merge behavior array
    function __behaviors($behaviors = null) { if ($behaviors) { foreach ($behaviors as $index => $behavior) // Loop through method arguments { if (is_array($behavior)) // If method agrument is an array { if (count($behavior) > 0) // If method agrument contains more than one element then merge it with method arguments { $behaviors = array_merge($behaviors, $behavior); } unset($behaviors[$index]); // Unset method argument from method arguments } } return $behaviors; } }
    2) private function checks if behavior is disabled or not. if it is disabled, return array index else return false.
    function __is_disabled($behavior) { $disabled = false; if (isset($this->Behaviors->_disabled) and count($this->Behaviors->_disabled) > 0) { foreach ($this->Behaviors->_disabled as $key => $val) { if ($val == $behavior) { $disabled = $key; break; } } } return $disabled; }
    3) adds behaviors in $this->Model->Behaviors->_disabled array
    function dontActAs()
    {
    $behaviors = $this->__behaviors(func_get_args());

    if ($behaviors and count($behaviors) > 0)
    {
    foreach ($behaviors as $behavior)
    {
    if ($this->__is_disabled($behavior) == false)
    $this->Behaviors->_disabled[] = $behavior; //add new one
    }

    $this->schema(true); //rebuild model schema
    }

    }

    4) removes from $this->Model->Behaviors->_disabled array
    function nowActsAs()
    {
    $behaviors = $this->__behaviors(func_get_args());

    if ($behaviors and count($behaviors) > 0)
    {
    foreach ($behaviors as $behavior)
    {
    $key = $this->__is_disabled($behavior);
    unset($this->Behaviors->_disabled[$key]);
    }

    $this->schema(true); //rebuild model schema
    }
    }

    I also found some functions in cake/libs/model/behavior.php “detach”, “disable” “enable” etc. not sure how they work. I’m still new to this cakephp thing.

Leave a Reply