Form Validation in CakePHP
Form Validation is an essential task in any online application, its also an incredibly dull, tedious and repetitive task but with CakePHP you can cut down the time and effort you spend validating your form data. In a standard PHP application you will normally display the form for user input, try to validate the data, if errors exist redisplay the form showing the errors whilst keeping the data that was already submitted and go through the process again until the data successfully validates.
In this article I'm going to through the basics of creating a Model, baking a sample application and explain how you can use CakePHP to automatically validate your data.
SQL, Model and Baking
For simplicity I am going to use the 'posts' table from my previous articles, if your starting from scratch the SQL statement for that table is shown below:
CREATE TABLE `posts` ( `id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY , `title` VARCHAR( 255 ) NOT NULL , `slug` VARCHAR( 255 ) NOT NULL , `text` TEXT NOT NULL , `created` DATETIME NOT NULL , `modified` DATETIME NOT NULL , `status` TINYINT( 1 ) NOT NULL DEFAULT '1' );
Next up is the 'post' model file, CakePHP works by defining validation rules in the Model file, if you have a look at the manual you will see that CakePHP has a number of preset validation rules which include VALID_NOT_EMPTY, VALID_NUMBER, VALID_EMAIL and VALID_YEAR. These are self explanatory and are used in the model like so:
// file: /app/models/post.php
class Post extends AppModel {
// name of model, good practice to include
var $name = 'Post';
// validation rules
var $validate = array(
'title'=>VALID_NOT_EMPTY,
'slug'=>VALID_NOT_EMPTY,
'text'=>VALID_NOT_EMPTY
);
}
An array is created called $validate which contains the name of the database field which requires validation and a rule that will be used to validate it. You can also use your own regular expressions but for the time being we will just use the preset ones. In this example the title, slug and text fields will need to contain information before the form will be successfully submitted.
Once the Model file is complete, bake the 'posts_controller.php' file and the view files. If you dont know how to do this then please read my previous article on using the Bake script. Check that everything is working by going to "http://cakephp/posts/add" and try to submit the form without entering anything. The form will be redisplayed with error messages that correspond to the validation rules that are defined in the model.
Validation
To make use of this automatic validation you need to use the "HTML" helper to create your forms, keeping with conventions you create a form input field using the $html->input('Model/fieldname', 'attributes') method and directly below use the $html->tagErrorMsg('Model/fieldname', 'error message to display') to display an error message if the form does not validate.
// file: /app/views/posts/add.thtml
// form input helper
echo $html->input('Post/title', array('size' => '60'));
// display an error message if data doesn't validate
echo $html->tagErrorMsg('Post/title', 'Please enter the Title.');
When calling the Model->save() method on your submitted data, CakePHP will automatically check your Model file and try to validate the data, if it validates the form data will be inserted into your database, a flash message is set and you will be redirected to the index page. If the data does not validate a flash message is set and the form is redisplayed and because you have used the html helper to build your forms, data that was submitted will re-populate the form.
// file: /app/controllers/posts_controller.php
// try to save the data, returns false if it doesn't validate
if ($this->Post->save($this->data)) {
// set success message
$this->Session->setFlash('The Post has been saved');
// redirect the user
$this->redirect('/posts/index');
} else {
// set error message
$this->Session->setFlash('Please correct errors below.');
}
Custom Validation
You can also use custom validation to do more extensive checks, for example i want to check that the post slug i have entered is not already in the database. If a post exists with the same slug name i want to redisplay the form with a custom error message. The code for doing this is as follows:
// file: /app/controllers/posts_controller.php
function add() {
// default slug error message
$this->set('slug_error', 'Please enter the Slug');
// if form is not empty
if (!empty($this->data)) {
// post is currently valid
$postValid = TRUE;
//if the post is not empty
if(!empty($this->data['Post']['slug'])) {
// try finding a post in the database with the slug name
$post = $this->Post->findBySlug($this->data['Post']['slug']);
// if a post has been found
if(!empty($post)) {
// invalidate the slug, this will enable the error msg in the view
$this->Post->invalidate('slug');
// set the slug error message
$this->set('slug_error', 'A Post with this slug already exists');
// invalidate the post
$postValid = FALSE;
}
}
// only continue if the post is valid
if($postValid) {
// try saving the post, will check validation rules in model
if($this->Post->save($this->data)) {
$this->Session->setFlash('The Post has been saved');
$this->redirect('/posts/index');
}
}
}
}
// file: /app/views/posts/add.thtml
// display the error message that was set in the controller
echo $html->tagErrorMsg('Post/slug', $slug_error);
Below is a screen shot of the custom error message in action:
When using custom validation you use the $this->Model->invalidate('fieldname') method to enable the error message in the view. Be careful when going down this route because when you use invalidate() it does not stop the save operation and so thats why i have used a boolean variable above to determine whether or not i can save the data. For example this will NOT work:
// if title is empty
if(empty($this->data['Post']['title'])) {
// invalidate field
$this->Post->invalidate('title');
}
// the save operation will go ahead regardless of the above invalidate()
if($this->Post->save($this->data)) {
// success
} else {
// error
}
Wrapping Up
This has been a pretty quick look at form validation in CakePHP but as you can see its very powerful and easy to use. Simply setup all your validation rules in the $validate array of each of your Model files and use the html helpers to generate error messages in your views and CakePHP will take care of the rest. If you want more complex validation rules you can build up your own regular expressions to validate the data or you can use your own custom validation in the controller to make checks e.g. in user registration making sure a username is not already taken in the database.
Copy of my CakePHP app directory with all the files in this article.
Comments
Jyothsna (08/07/2008 - 00:15)
As i'm beginner to Cake PHP...this is very helpful to me
Mike (20/08/2008 - 20:38)
Nice Tutorial! Very easy to follow!
James, you can also use the isUnique rule to check that the slug doesn't clash.
james (21/08/2008 - 04:53)
@Mike: thanks for that the "isUnique" is probably a better way of doing things.
Ayo (05/12/2008 - 22:54)
Using "tagErrorMsg" in my blog application doesn't work well for me but I Love your Form Validation. And I was able to tweak the "tagErrorMsg" usage a little to:
if(isset($error)) {
echo "<div class='error-message'>" . $error ."</div>";
}
echo $form->input('Post/title', array('size' => '60'));
echo $form->error('User.password', null, array('class' => 'error-message'));
Which works perfectly using it with your $error session style.
James (08/12/2008 - 04:54)
@Ayo: Hey thanks for commenting and I'm glad its helped you out.
Krupesh (21/01/2009 - 04:22)
i'm beginner to cakephp and it's really helpful tutorial for everyone
Prabhash (24/02/2009 - 03:57)
I am getting an error while using your code.I am using cake v1.2.1.8004.
Method HtmlHelper::input does not exist [CORE/cake/libs/view/helper.php, line 143]
can u suggest how to overcome this error.
James (06/03/2009 - 01:15)
@Prabhash: hmmm very strange error, not too sure what's going on there. Have a look in the helper.php to make sure that the function is defined.
Matt (17/05/2009 - 03:30)
Hi Prabhash, just thought I'd pop in and help out. Although I'm sure you've found the answer by now....
This article is written with cakePHP 1.1 in mind. One of the biggest changes in cakePhp 1.2 is the introduction of the FormHelper ($form) which removes all form creation from the HTMLHelper mentioned here. Most of the functions should still work though....
Hope that helps ;o)
Atea Webdevelopment (26/05/2009 - 07:14)
Very usefull, thanks.
neel (29/09/2009 - 06:09)
Nice Article for cakephp validation. I will like to read more cakephp stuff on your blog
Hasan (09/02/2010 - 02:59)
Hi, using this showing the following warning:
Warning (512): Method HtmlHelper::tagErrorMsg does not exist [CORE\cake\libs\view\helper.php, line 143]
Any solution?
----------
Thanks
James (14/02/2010 - 22:33)
@Hasan: This was written using an old version of CakePHP, probably 1.1.x and the tagErrorMsg() method has been depreciated in the new versions. I'll try and update it when I have the time.
dzEo (15/02/2010 - 16:06)
Hi guys, i'm trying to get error messages translated.
My model :
var $validate = array(
'name' => array(
'emptyField' => array(
'required' => true,
'allowEmpty' => false
)
),
My view :
echo $form->input('Contact.name', array('label' => false,
'div' => false,
'class' => 'contactForm',
'size' => 25,
'error' => array(
'emptyField' => __('Veuillez saisir votre nom', true)
)
));
Then my controller :
if ($this->Contact->save($this->data)) {
$this->Session->setFlash('Message envoyé !', 'growl');
$this->redirect(array('action'=>'index'));
} else {
$this->Session->setFlash('Erreur !', 'growl', array('type' => 'error'));
}
The prob is that the error message called by my own 'emptyField' rule doesn't work.
Any suggestions ?
Thanks.