Displaying form errors as a list in CakePHP

This article is over a year old and may contain outdated information.

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 UsersController (do note that I am not using the AuthComponent, this is just a rough example). Lets begin by creating a file and placing it in your views/elements/errors.ctp folder.

<?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 UsersController.

<?php
class User extends AppModel {

	public $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 RequestHandlerComponent 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
public 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
public 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

  • Excellent, that's what I was looking for and spent days until I found this! Thanks, Mimi
  • You can also incorporate it with AuthComponanet.
    Just set this ctp as flash element and using Session->setFlash() with $params pass all the listed items.

    Works nice for me.
    Marius
  • @Christopher Vrooman,

    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
  • Miles,

    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.
    Christopher Vrooman
  • Error management in cake is bit weird.
  • Thank you thank you thank you - this is fantastic and just what I need.

    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.
  • thanks for the explanation. grouping all of the errors together is a much better idea. i also didnt know about the validationerrors property. that will be useful.

    thanks again.
    Saliem
  • Hi, good post. I have been pondering this issue,so thanks for sharing. I'll definitely be subscribing to your blog.
  • Thanks Miles, this is just what I needed!
  • thanks!

    Also great to use with AJAX to send all the validation errors as xml or json!
  • Thanks so much, this is exactly what I needed. Errors can be a bit voodoo if you haven't really played with them.