$model->validate = $model->{$param};
} else {
e('MultipleValidatableBehavior: Please define ' . $param .' in your model ' . $model->alias . '. Be sure useValidationRules call in the controller "' . Router::getParam('action') . '" is correct.');

Dardo Sordi said on July 31, 2008

Jonathan, I'm using it in many models, if that weren't the case it would be overkill to make a behavior, I'll just put the methods in the user model as in your first post.

For example it's useful for me in form wizards.

Anyway I like your methods names (api?) and having every rule set in a separate variable improves readability. I'm going to change my behavior (not the one in the article) as I see your approach cleaner.

And for the action coupling thing, I don't see me using it so I'll leave that out.


keymaster said on August 03, 2008

Without trying it out (so I apologize in advance if this is nonsense) but, is it not possible, at least in php5, to eliminate the need for calling resetValidationRules()?

Could one not override the model Save() in appModel something like:

function Save(...){
- your before validate stuff.
- your reset validate stuff.

Dardo Sordi said on August 03, 2008

It should be implemented as unbindModel() with two modes: persistent mode and a non-persistent mode.

Sean said on August 03, 2008

I'm seemingly forever comparing Code Igniter and CakePHP. I use a custom framework at my day job, but for personal projects, I'm still researching. You showed something that I find the 2 frameworks doing differently. In CakePHP, the validation is taken care in the Model. In CodeIgniter, validation is done in the controller before being given to the Model. Which do you think is more proper, or could you justify the logic belonging in either class?

Jonathan Snook said on August 03, 2008

@keymaster: yes, it'd be possible but I don't love that approach.

@Sean: I think it makes more sense in the Model. With CI, they have a validation class, which I actually like. With CakePHP, I think the Model should use a Validation object to determine how data should be validated. This would allow the validation object to be subclassed, instead of defining custom function names. But maybe for CakePHP 2.0. :)

Sean said on August 03, 2008

As I was thinking about it, I agree. Putting it in the model means you don't have to remake validation rules if you make another function to insert data into the database.

I really like CI's Validation class. I admit, I ripped it off and threw it into my framework at work. What would it take to make the same thing in Cake? It seems Cake is pretty mod-able. I'd be interested in throwing that together, as I get more and more sold on Cake.

insic said on August 07, 2008

wow! its been a while im not visiting your site and now you got a series of article for cake php which is the framework i am using aside from zend and code igniter. I take some time reading all your articles about cake. thanks for the nice article snook.

Heather said on August 09, 2008

Thank you so much! This is *exactly* what I needed.

Jonathan Snook said on August 09, 2008

Sean, turns out upon further inspection that CakePHP does have its own Validation object. It just uses the validation rules defined in the Model to validate against the functions defined in the validation object. There could be some additional separation but with that said, I'm not sure much would be gained.

Erick wilder said on August 30, 2008

Just a note..

You can also define a specific validation set to be used with $this->ModelName->useValidationRules('ExampleSet') which will look for a validation property called validationExampleSet. If the property doesn't exist, you'll get an error, so be careful to match the name.

I think that the Behavior will look for properties prefixed with 'validate' and not 'validation'. On the example the correct property to be set on the Model object is "$validateExampleSet".

Great work!

Erick Wilder said on September 01, 2008

After trying to run the acl command line utility I got some errors, because the class Router is not loaded. I did some modifications to fix up the shell commands:

$actionSet = 'validate' . Inflector::camelize(Router::getParam('action'));
$actionSet = 'validate';

Chris Yates said on November 10, 2008

Continuing the discussion on coupling between the controller and model, here's my idea for loosely coupling them.

The default rules are placed in the model. Action specific rules are specified in the controller meaning the model doesn't need to know about controller actions, but it gets the appropriate rules to validate against.

Action specific validation rules go in the $validate array in the controller:

var $validate = array(
  'first_action' => array(validation rules for first_action),
  'second_action' => array(validation rules for second_action),

I needed to change the Behavior slightly. In the beforeValidate function the code for the if statement becomes:

    if (isset($this->__useRules[$model->alias])) {
      $model->validate = $this->__useRules[$model->alias];

because as you'll see below the Behavior now stores the actual rules.

I then put the following in the beforeFilter callback of my app_controller:

    if (isset($this->validate[$this->action])) {

This checks for a set of validation rules for the action. If they are set they are passed to the Behavior which then passes them to the model when it validates.

This way the model doesn't know anything about the controller or it's actions and is given the appropriate set of validation rules as needed.

Sorry, comments are closed for this post. If you have any further questions or comments, feel free to send them to me directly.