Optionable Behaviour

These last days I’ve been flirting with NoSQL databases, particularly MongoDB which has native support on the Lithium Framework. I was doing a lot of rewriting on an application that runs CakePHP + MySQL and I wish I could have MongoDB’s flexibility in order to just throw a new key in a form and have it saved for me. It would save me a lot of time and effort so I made this behaviour.
The Optionable Behaviour is a CakePHP behaviour that allows you to save fields not declared on a given DB design [I guess that any RDBMS that Cake supports will work just fine]. The code for this behaviour is at the end of this post.

So, how does it work?
First of all, this is a highly experimental behaviour. I mean… It’s not been used on production yet and I bet a lot of things could be better, but as far as I’ve tested it, it works just fine. It’s not perfect, and I wish some things could be done differently, but that is how it goes.
You use it just like any other behaviour. Put the file on you models/behaviours/ folder, add this to the model that you want to behave, just say:

public $actsAs = array('Optionable');

You will need to create a new table on you database. Something like this:

CREATE TABLE IF NOT EXISTS `options` (
`id` int(11) NOT NULL auto_increment,
`model` varchar(32) NOT NULL,
`related_id` int(11) NOT NULL,
`opt_key` varchar(32) NOT NULL,
`opt_value` text NOT NULL,
PRIMARY KEY (`id`),
KEY `opt_key` (`opt_key`)
);

And an empty model like:

class Option extends AppModel { }

If, for some reason, you can’t create the table with the name ‘options’, don’t worry. Just use another name and them config the behaviour like this:

public $actsAs = array('Optionable' => array('model' => 'SomeOtherModelName'));

Now, let’s imagine that you have a model named Blog which has three columns on it’s table: id, title and content. You can create a form that goes like this:

echo $form->create('Blog');
echo $form->input('id');
echo $form->input('title');
echo $form->input('content');
echo $form->input('mood');
echo $form->end('Send');

See that I have a new key ‘mood’ on the form. That will be automatically saved to the options table and when you make a query on the Blog’s model the ‘mood’ key should appear just like it was a native key. What the behaviour does is to compare the model’s schema with the fields being send by the form. It will segregate those fields that doesn’t appear on the schema and save them as options. If it happens that you are sending non schema fields but doesn’t want to save them as options, you just need to declare some exceptions on the behaviour. Like this:

public $actsAs = array('Optionable' => array(
'model' => 'MyCustomModel',
'exceptions' => array('comparepassword', 'verifystring')
));

The behaviour have some limitations though:

  • The behaviour won’t work on creation [yet], just on updates [FIXED]
  • If you are using the Containable behaviour, you will need to contain the model being used to save the options
  • There is no way [yet] to filter an entry by an option key

So, I guess this is it. If you have any idea on how to make this work any better, please, share with us! Here is the code: