Displaying form errors as a list in CakePHP
Tuesday, January 6th 2009, 2:11am
Topics: Tutorials, CakePHP
Tags: CakePHP, Tutorial, Errors, Request Handler, Validation, Model
Comments: 12
Permalink -
Tinylink
When a form is submitted in CakePHP, any errors that fail the model validation are displayed after each respective input field. But if you're like me and really dislike that structure and output, and would rather have your errors displayed in a list above the form (an example would be my contact form), then this is how you would achieve it. In the example we will be doing a login for the Users controller (do note that I am not using the Auth component, this is just a rough example). Lets begin by creating a file called errors.ctp and placing it in your views/elements/ folder.
Validation in the Model
For any form validation to work, we must declare a validation scheme within the respective model, in our case its the User model. Since this is a login form, the only validation we will need is for a username and password. The rules we will be applying is that both the username and password must not be empty. Once we have the validation setup, the next step is to setup the Users controller.
Setting up the Controller
In the login action we will be using the RequestHandler component to check for a form submission (this is a better approach then doing isset/!empty on $this->data). If a post is successful, we will save the form data to the User model to run it against the validation. Lastly, we need to be able to display the errors in the view, so we will set() them to $errors by calling the $this->User->validationErrors. The $errors variable is then passed to the errors.ctp element template, which in turn displays all the errors found.
The two most important things to note is $validationErrors and the invalidate() method. The $validationErrors property is a list of all the error messages in the form for the corresponding input fields; it's called from each respective model. The method invalidate() is used to trigger a custom error that is not part of the $validate scheme for the model. Now lets look back at the controller where the verifyLogin() method is called. If that method fails (returns false), the login fails and no processing is made. On the other hand if the method returns true, lets continue and log them in.
Displaying my errors in the View
Now that we have the validation set, the errors template created and the form processing complete, the last task is to display the errors in the view. Since the controller is already setting the errors into the view, we just need to include the element, and to do that we will use the $this->element() method within the view. Just place this element in the location you want your errors to display.
Now if you have tested the form, you will notice that the errors are still showing up after the input field. To fix this, we have to disable the errors on the input by setting it to false.
Conclusion
This was a bit more lengthy then I expected, but I hope it enlightened you on how to use and display form errors. You also received a bit more knowledge on how the invalidate() and invalidFields() method work (hopefully ;)). If you have any questions please comment or send me an email, or take a gander at the CakePHP API and Cookbook.
<?php // views/elements/errors.ctp
if (!empty($errors)) { ?>
<div class="errors">
<h3>There are <?php echo count($errors); ?> error(s) in your submission:</h3>
<ul>
<?php foreach ($errors as $field => $error) { ?>
<li><?php echo $error; ?></li>
<?php } ?>
</ul>
</div>
<?php } ?>Validation in the Model
For any form validation to work, we must declare a validation scheme within the respective model, in our case its the User model. Since this is a login form, the only validation we will need is for a username and password. The rules we will be applying is that both the username and password must not be empty. Once we have the validation setup, the next step is to setup the Users controller.
<?php
class User extends AppModel {
var $validate = array(
'username' => array(
'notEmpty' => array(
'rule' => 'notEmpty',
'message' => 'Your username is required'
)),
'password' => array(
'notEmpty' => array(
'rule' => 'notEmpty',
'message' => 'Your password is required'
)),
);
}
?>Setting up the Controller
In the login action we will be using the RequestHandler component to check for a form submission (this is a better approach then doing isset/!empty on $this->data). If a post is successful, we will save the form data to the User model to run it against the validation. Lastly, we need to be able to display the errors in the view, so we will set() them to $errors by calling the $this->User->validationErrors. The $errors variable is then passed to the errors.ctp element template, which in turn displays all the errors found.
<?php // controllers/users_controller.php
function login() {
if ($this->RequestHandler->isPost()) {
$this->User->set($this->data);
if ($this->User->validates()) {
if ($this->User->verifyLogin($this->data)) {
// Process information
}
} else {
// Didn't validate
}
}
$this->set('errors', $this->User->validationErrors);
}
// models/user.php
function verifyLogin($data) {
$user = $this->find('first', array(
'conditions' => array(
'User.username' => $data['User']['username'],
'User.password' => md5($data['User']['password'])
)
));
if (!empty($user)) {
return $user;
} else {
$this->invalidate('', 'No user was found with those login credentials');
}
return false;
} ?>The two most important things to note is $validationErrors and the invalidate() method. The $validationErrors property is a list of all the error messages in the form for the corresponding input fields; it's called from each respective model. The method invalidate() is used to trigger a custom error that is not part of the $validate scheme for the model. Now lets look back at the controller where the verifyLogin() method is called. If that method fails (returns false), the login fails and no processing is made. On the other hand if the method returns true, lets continue and log them in.
Displaying my errors in the View
Now that we have the validation set, the errors template created and the form processing complete, the last task is to display the errors in the view. Since the controller is already setting the errors into the view, we just need to include the element, and to do that we will use the $this->element() method within the view. Just place this element in the location you want your errors to display.
<?php echo $this->element('errors', array('errors' => $errors)); ?>Now if you have tested the form, you will notice that the errors are still showing up after the input field. To fix this, we have to disable the errors on the input by setting it to false.
<?php echo $form->input('username', array('error' => false)); ?>Conclusion
This was a bit more lengthy then I expected, but I hope it enlightened you on how to use and display form errors. You also received a bit more knowledge on how the invalidate() and invalidFields() method work (hopefully ;)). If you have any questions please comment or send me an email, or take a gander at the CakePHP API and Cookbook.
12 Comments
twitter.com/k00k
Jan 27th 2009, 16:41
Feb 12th 2009, 19:42
Also great to use with AJAX to send all the validation errors as xml or json!
rahil.ca
Apr 28th 2009, 10:28
jpweightlossblog.com
May 3rd 2009, 19:50
Aug 6th 2009, 21:10
thanks again.
theworldofdan.co.uk
Nov 7th 2009, 10:10
Not only does it solve a usability problem, but it also means that I'm not going to have to have lots of empty $form->error() elements scattered all over my page.
teamaguilar.com
Feb 16th 2010, 12:37
Feb 17th 2010, 12:14
how do you handle validation when you're using two or models in the form? I've run into the situation where only the first model's errors show up. Then, once they all validate properly, the next model's errors appear and so on. It would be nice to see all the errors at once to avoid having to keep resubmitting the form over and over.
itworks.lt/blog
Oct 14th 2010, 01:00
you should use $model->saveAll($this->data, array('validate' => 'first',));
This way all the models are validated prior to saving them one-by-one. Of course, you must be using $model->invalidate(); correctly.
If you don't want to use saveAll(), I guess you will have to play with $this->model->validationErrors. But that's nasty :)
Apr 6th 2011, 13:31
Just set this ctp as flash element and using Session->setFlash() with $params pass all the listed items.
Works nice for me. :)
taxisamba.com
Dec 6th 2011, 02:22