Introducing two CakePHP behaviors

Today I want to introduce two new CakePHP behaviors.

Jsonable Behavior

This is not so new, of course. Already existed as a basic version in the Bakery.
I enhanced it to work with more than just plain arrays and added some more functionality.

The behavior can be found in the Tools Plugin.

In my first scenario where I used it, I had a geocoder behavior attached to the model which returned an array. I wanted to save all the returned values, though, for debugging purposes in a field "debug". By using the following snippet I was able to do exactly that.

public $actsAs = array('Tools.Jsonable' => array('fields' => array('debug'), 'map' => array('geocoder_result'));

I could access the array in the view as any other array since the behavior re-translates it back into an array on find().
Note: the mapping option is useful if you want to rename certain fields. In my case the geocoder puts its data into $this->data[‘Model’][‘geocoder_result’].
I might need to access this array later on in the model. So I "jsonable" it in the "debug" field for DB input and leave the source field untouched.

That’s all very nice. But what if needed something more frontend suitable. I want to be able to use a textarea field where I can put all kinds of params which will then also be available as array afterwards (as long as you are not in edit mode, of course).

In our add/edit actions we need to switch to param style:

$this->Model->Behaviors->unload('Jsonable');
$this->Model->Behaviors->load('Tools.Jsonable', array('fields' => 'details', 'input' => 'param', 'output' => 'param'));

The form contains a "details" textarea field. We can insert:

param1:value1|param2:value2

In our views we get our data now as array:

array('Model' => array(..., 'details' => array('param1' => 'value1', 'param2' => 'value2')));

And, of course, as third use case we can also simulate an ENUM by using:

$this->Model->Behaviors->unload('Jsonable');
$this->Model->Behaviors->load('Tools.Jsonable', array('fields' => 'tags', 'sort' => true, 'input' => 'list', 'output' => 'list'));

Note: The default value in the model itself still needs to be array in order to transform them into a list on find()!
That’s why we only override them in the form views.

In our textarea we can now type:

dog, cat, cat, fish

In our views we would result in (var $data[‘Model’][‘tags’])

array('cat', 'dog', 'fish');

Note the cleanup automation you can additionally turn on/off.
There are more things to explore. But I will stop here for now.

Yes – you could make a new table/relation for this in the first place. But sometimes it’s just quicker to create such an enumeration field. Bear in mind: It then cannot be sorted/searched by those values, though.
For a more static solution take a look at my Static Enums.

Confirmable Behavior

This I invented a while ago working on a registration page. After I needed the same functionality on other sites, as well, I started to build a behavior out of it. Now I don’t have to repeat myself anymore.
On Github you can find the Confirmable behavior.

Example Usage in an action:

// if posted
$this->Model->Behaviors->load('Tools.Confirmable', array('field' => 'confirmation', 'message' => 'My custom message'));
$this->Model->set($this->data);
if ($this->Model->validates()) {
    // OK
}

In the corresponding view of this action:

echo $this->Form->input('confirmation', array('type' => 'checkbox', 'label' => __('Yes, I actually read it')));

The user has to check the toggle field provided. Otherwise validation will fail.

Some notes on behavior development

Make sure you return the right boolean value in those beforeValidate() methods etc.
Usually this should be TRUE even if it fails.
The reason is you want to display all errors at once in the form. If you return FALSE too soon, the other validation methods won’t get triggered and you have a staged validation which is a bad thing.
So return TRUE. And don’t worry, as long as there are errors the save method won’t get triggered.

As you can see the settings are always "per Model->alias". This way multiple models with the same behavior won’t interfere which each other (since they all use the behavior instance – yes, that’s the CakePHP way of saving resources, I guess).

Update 2013-08

I upgraded the examples from 1.x to 2.x code.

2014-01-22

Modified the methods to 2.4/2.5 syntax: attach=>load and detach=>unload

CakePHP 3

This article is 2.x only.
For CakePHP 3 please see the 3.0 Tools Plugin documentation for the upgraded JsonableBehavior.

2 Comments

  1. Hi,

    Thanks for the awesome post!

    Just a note: in the 2nd last code snippet the attach method is not called correctly. Should be:

    attach ( string $behavior , array $config = array() )

    Tested in CakePHP 2.3.1. Might have worked for some previous CakePHP versions?

    Thanks again for your awesome blog!

    Greetings,
    D

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.