Displaying form errors as a list in CakePHP
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.