CakePHP: Data Validation

Validation is one of the basic and most tedious tasks in application development. If there was ever a reason to use a framework this would be it. There are two ways that appear to be advocated by the CakePHP site and while both have benefits, I'm using a slightly different approach.

Auto-validation

Probably the easiest thing to use is the built-in auto-validation. This is controlled using a $validate variable inside the model. You can specify requirements for each field using a number of predefined constants. Here's a quick example:

class Profile extends AppModel
{
    var $name = 'Profile';
    var $validate = array(
      'firstname' => VALID_NOT_EMPTY,
      'lastname' => VALID_NOT_EMPTY,
      'password' => VALID_NOT_EMPTY
    );
}

As you can see, I've specified a few fields that must have a value in order to validate. This is great for quick and easy validation but you'll quickly hit the limits of this. Case in point would be requiring a valid email address. You can't specify more than one requirement for a single field. That means that an email could be a required field or a valid email but not both.

The alternative to this is to use custom validation. The wiki seems to recommend overriding the beforeSave() method of the model and to do your validation manually. Each field that fails can be easily invalidated using the invalidates method of the model. From within the model you can invalidate a field like this:

$this->invalidate('email');

To invalidate from the controller, you can do it like this:

$this->Profile->invalidate('email');

The problem with overriding the beforeSave is that any validation that fails from the $validate variable will mean beforeSave will never fire.

validates()

The alternative is to override the validates method. The validates method in the base model essentially returns true or false as to whether the model validates before saving. So, by overriding this, we can add our custom validation and then return true or false after that. Let's extend the initial example:

class Profile extends AppModel
{
    var $name = 'Profile';
    var $validate = array(
      'firstname' => VALID_NOT_EMPTY,
      'lastname' => VALID_NOT_EMPTY,
      'password' => VALID_NOT_EMPTY
    );

    function validates()
    {
        $profile = $this->data["Profile"];
        if($profile["password"] != $profile["password2"]){
            $this->invalidate('password2');
          }
        
          $errors = $this->invalidFields();
          return count($errors) == 0;
    }
}

The validates method starts off by loading the profile data in, comparing the two password fields and then invalidating one of them if they don't match (basic password confirmation). Then I count the number of errors and if there are none, the comparison will return true, otherwise it'll return false indicating the model didn't validate.

Note: This is for CakePHP version 1.1.4.3104 . It may be in a full 1.x release but changes still seem to be coming fast and furious with version 1.1.5.3148 already out a week later, version 1.2 just around the corner and version 2.0 coming after that. There's some nice stuff in here but it still feels like beta.

Published June 19, 2006 · Updated June 19, 2006
Categorized as CakePHP
Short URL: https://snook.ca/s/617

Conversation

34 Comments · RSS feed
Richard@Home said on June 20, 2006

Very useful article jonathan. I'm still stumbling around with CakePHP (not having any real projects to use it or the time the play). How about posting this to the wiki?

Steve Tucker said on June 20, 2006

Good article. Another effective method around an age old problem.

Jonathan Snook said on June 20, 2006

Richard: I wouldn't want to post to the wiki as I don't feel that I have enough ownership or knowledge of the application yet to be contributing directly. On a related note, there are also plans to do away with the wiki.

sosa said on June 20, 2006

You could also have a different error tag in your view for every possible validation error, so it can be more verbose:

$this->invalidate('passwordsDontMatch')

But I still think that using the beforeSave callback (along with all the predefined callbacks) is the way to go.

Jonathan Snook said on June 20, 2006

Sosa: the problem with using beforeSave is that you then have to rely on it for ALL of your validation as opposed to offloading some of the basics.

The alternative is to adjust the Cake core to always execute beforeSave, no matter what — which probably isn't a bad idea.

Thanks for the error tag tip. :)

gwoo said on June 20, 2006

dont forget about beforeValidate() this will allow you to use both the simple method and any custom validation via invalidate(). I wrote a quick example to the google group a few weeks back.

Besides that...great stuff you are putting together on Cake.

EJ said on June 20, 2006

Wow. Seems I need to give this a try. Jonathan, my object oriented programming is kind of weak. Should I avoid this at all costs? I like PHP but haven't worked with it at this level. What's your opinion?

As always, an informative article.

Mike said on June 21, 2006

I have used beforeValidate() and invalidate() as gwoo suggests

http://groups.google.com/group/cake-php/browse_thread/thread/89acef2ed01dd7ee/e5fe6d771038e51a#e5fe6d771038e51a

Jonathan Snook said on June 21, 2006

EJ: your decision to use Cake should be based on whether you have the time to invest to learn the framework. I'm not sure if a strong knowledge of object-oriented programming is necessary but it certainly helps. It highly depend on the type of application you're putting together.

cody lindley said on June 22, 2006

Sorry, this is a little off topic for this article but…I thought you were a rails guy? Can you give a short talk on why you have chosen to use Cake? I'm very curious as I am thinking about learning it, or Django.

Jonathan Snook said on June 22, 2006

The decision to go with Cake over Rails or Django had a lot to do with the available developer pool. There's a larger pool of PHP developers that can take over the development of the project once it has gone live. The same couldn't be said of ruby or python developers.

Also, I'm a PHP developer and haven't tried ruby or python. Since I'm heading up the development of this project, it was easier to use a framework whose base language I understood over one I didn't.

I would like to try out Rails and Django but it'll have to wait until I have the time to invest in them for personal projects.

Walker Hamilton said on June 24, 2006

Can someone reading this (maybe Jonathan) speak to the display and styling of errors output by the validator?

I built a simple app with validation in the controller for the registration page. How can I get the form to essentially re-render and display errors at each input item when there are validation errors?

Walker Hamilton said on June 24, 2006

p.s.

I ask this question here because I come from a CSS/XHTML background when it comes to code (those are my forte) and I am an Information Architect by study, so I want to display errors with the form in question, not using back or simply displaying them at the top (or bottom) of a form.

Jonathan Snook said on June 24, 2006

Walker: that's probably the easiest thing to do with CakePHP. Basically, for each field that you invalidate, you can use a helper in the view to display the error message. You'll notice how in the example in the article, I invalidated a field called 'password2'. I could display an error message for this field like this:

<?=$html->tagErrorMsg('Profile/password2',
'Sorry, the passwords don't match');?>
Emil said on June 26, 2006

Yesterday I downloaded cakephp, it seemed to be a nice framework (based on articles here at snook). The only problem I found was that they haven't included a template system. My current framwork is using flexy and it works pretty good. I just hate to write php and html in the same document, to many <?php ?> etc. RoR has liquid, how about cake?

Jonathan Snook said on June 26, 2006

Emil: there really isn't a template system beyond what's part of the view system. However, you are certainly free to use another template system like Smarty along with CakePHP. There's a tutorial on the wiki that explains how to do just that. My personal feeling is that the difference between Smarty and just using some PHP if statements or for loops is extremely minor.

Emil said on June 26, 2006

Jonathan: Do you really think its no difference? I'v done 3-4 projects with template systems and I can't go back, the view code is much easier to read, especially when you have large view blocks. From smarty.php.net: "{$title} is less extraneous than <?php echo $title; ?>". Don't you agree with that?

Jonathan Snook said on June 26, 2006

I agree with your example, which is why I use the short form of <?=$title?>. Much more succinct.

Larry E. Masters said on June 26, 2006

@Emil,

One thing you will notice in cake is I have wrote the view class to allow an easy way to extend it. While I have no plans to add a templating system to cake itself, since PHP is a templating language. You are free to extend it and use your own.

I have never used flexy, but I did write a Smarty view class that should give you an start form impementing one for flexy, and I would suggest submitting it as a snippet on the CakeForge site where others my find it useful.

Smarty View class
http://cakeforge.org/snippet/detail.php?type=snippet&id=6

@jonathan,

If you plan to distribute your application I would suggest not using short tags, since some host may turn them off.

Jonathan Snook said on June 26, 2006

Larry, re:short tags: agreed. The app I'm developing won't be for distribution. :)

Dan said on July 09, 2006

"RoR has liquid"

Emil, RoR is using eRuby for views, which is ruby embedded in html.

Liquid (which is a ruby port of the django templating) can be used as a plugin, and is basically only used when wanting to do some sort of theming...

Liquid was developed for the use of theming of individual Shopify shops... I dont know of many products/applications actually using liquid...

Emil said on August 25, 2006

Ok, I'm sorry - never really tried RoR. Just got amazed over Shopify when it was in beta.

Larry & Jonathan: I'v started develop my own personal homepage/blog and I will do it without any template engine but with cake! I'll be back when I'v tried it for "real".

Tobi said on September 03, 2006

What I am asking myself is, how does cake handle the difference between an INSERT and an UPDATE? For example: I defined the password has to be filled, but by editing the form I don't want the user to fill in the password, just if he wants to.

Jonathan Snook said on September 05, 2006

Tobi: when using Modelname->save($data), it'll do an INSERT if it doesn't have a model ID and an UPDATE if it does. If you don't want the user to have to enter in the password you can either (A) spit their password back into the form (assuming you're going unencrypted) or (B) simply check if the password field is empty on edit and if so, unset that variable. I believe CakePHP will only update fields that have data in them.

Nick Busey said on October 04, 2006

I don't know where you got the idea that you can't declare more than one requirement per field. The following is perfectly valid:

class Profile extends AppModel
{
var $name = 'Profile';
var $validate = array(
'firstname' => VALID_NOT_EMPTY,
'lastname' => VALID_NOT_EMPTY,
'password' => VALID_NOT_EMPTY,
'email' => VALID_NOT_EMPTY,
'email' => VALID_EMAIL
);
}

Though unnecessary, just to demonstrate. You can use Cake's validation for anything, no need for custom functions.

Jonathan Snook said on October 04, 2006

Nick: those issues may be resolved in recent builds but wasn't when this article was posted. That's where I "got the idea".

Chris Hoeppner said on January 03, 2007

I have written a custom validates routine that checks for any declared "validateFieldName()" functions and runs them, passing any error messages into an array called (doh!) $errorMessages. After all found functions are called, it checks to see if there's any messages in $errorMessages, and returns false (preventing the save operation) if there are some. If I wanted, I could have called the parent::validates function before returning anything, but I rarely use the $validate array for anything.

@Nick: That's nice if all you need is to check if the field is not empty or is a valid email. I use those constants in my validate functions too. But what about checking if someone is double-registering, of if the two password fields match.

Code igniter has a more flexible validation system. Not that I prefer CI, it's just that.

Cake for everyone!

Jim Zaghini said on January 09, 2007

Thanks mate,

You have saved my feeble brain. It will not be exploding today

:)

Marko Samuli Kirves said on May 04, 2007

Invalidation from the controller did not work in 1.2. Saving the model clears fields in validationErrors array.

I solved this by setting new validation rules that fail with custom messages if I found in controller that data is not valid.

Jonathan Snook said on May 04, 2007

@Marko: likely with the next alpha release of 1.2 you should be able to do custom messages with custom validation. It should be sweet.

Harro said on May 09, 2007

I seem to have a problem accessing $this->data[] in the model. Do you know what this issue could be?

Thanks, great work your doing here.

Jonathan Snook said on May 10, 2007

If you don't have anything in $this->data, are you sure the form is set up correctly? It's hard to say but if you haven't already, check out the Google group and post your question there. You'll probably have more success in getting your question answered.

Norm said on September 24, 2007

Say if you want to use $validates but not worry about every field in the $validates array being required. Do something along these lines in your model:

function beforeValidate(){

foreach($this->data['ModelName'] as $k => $v){
if(empty($v)){
unset($this->validate[$k]);
}
}

return true;
}

It will only validate when the field is not empty.

Brian Katzung said on January 28, 2009

Regarding templating vs PHP tags (from June 26, 2006):

I came to PHP from Perl, so it was natural for me to think in terms of heredocs:


echo <<<HTML
<title>$title</title>
HTML;

While it doesn't deal with iteration or other "complex" coding, it does take care of all those finger-busting tags for simple variable substitution in large HTML blocks.

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