Validate single fields trough AJAX with CakePHP

Today i was busy making a form for a signup procedure and i tought: “Lets make it a bit easier for the customer by adding some real-time validation”. Of course this is a piece of cake with.. Cake!
Enabling JavaScript
First we need to load the script.aculo.us libaries.
Download them from script.aculo.us, extract the files and copy prototype.js in /lib to your cake js folder (app/webroot/js) and all the files in /bin to the cake js folder.
Then add the following to your default.ctp (app/views/layouts/default.ctp (if it’s not there create one))
<?php
if(isset($javascript))
{
echo $javascript->link('prototype');
echo $javascript->link('scriptaculous.js');
}
?>

And in your controller (app/controllers/customers_controller.php) load JavaScript (note: you could also put this in app_controller to make it available in every view instad of putting it in every single controller).
var $helpers = array('Html', 'Javascript', 'Form', 'Ajax');
Enabling RequestHanlder
Now we need to make shure we serve the right content with the right request (return xml if it’s requested).
Add the following to your routes file (app/config/routes.php)
Router::parseExtensions();
Now open your controller (app/controllers/customers_controller.php) and add the following line:
var $components = array('RequestHandler');
We can now use the power of requesthandler \0/
Model
Lets make some basic validation rules: (app/models/customer.php)
var $validate = array(
'initials' => array('length' => array('rule' => array('minLength', '1'), 'message'=>'This field cannot be empty')),
'lastname' => array('length' => array('rule' => array('minLength', '3'), 'message'=>'This field cannot be empty'))
);

Now lets make some actions!
Controller
The add action in the controller (app/controllers/customers_controller.php)
function add() {
// Ajax validation of single fields, check if xml is asked
if ($this->RequestHandler->ext =='xml') {
// If we have data, process it. If not send back an error.
if(!empty($this->data['Customer'])){
$this->cleanUpFields();
// Validate the customer, if it's ok, show no errors. If not ok, show errors
if ($this->Customer->create($this->data['Customer']) && $this->Customer->validates()) {
$this->set('error', '0');
$this->set('message', '');
} else {
$errorMessages = $this->validateErrors($this->Customer);
$this->set('error', '1');
$this->set('message', array_shift($errorMessages));
}
} else {
$this->set('error', '1');
$this->set('message', 'No data sent');
}
} else {
// Normal validation and save
if(!empty($this->data)) {
$this->cleanUpFields();
$this->Customer->create($this->data);
if($this->Customer->save($this->data)) {
$this->redirect(array('action'=>'thankyou'));
} else {
$this->Session->setFlash('Woot, something went wrong, please check the red fields!');
}
}
}
}

What we did is check if a xml file was requested by the client (this is what we use in the ajax request), if so, check only a single field and return if there is an error or not.
The XML views
In your customers view folder (app/views/customers) create a new folder named xml. In there create a new file called add.ctp.
This is the file the controller uses when it wants to send back a xml view.

<?php echo $error; ?>
<?php echo $message;?>

We also need to create a layout for the xml view, in the layouts folder there should be a folder named xml (app/views/layouts/xml)
Open it and create a file called default.ctp with the following content
<?php header('Content-type: text/xml'); ?>
<?php echo $content_for_layout; ?>

The add view
Now lets make a form (app/views/customers/add.ctp)
<?php echo $form->create('Customer');?>

Name

<?php echo $form->input('initials', array('label'=>'Initials', 'maxlength'=>'8', 'size'=>'8', 'class'=>'req', 'div'=>false)); ?>

<?php echo $form->input('lastname', array('label'=>'Last name', 'size'=>'20', 'class'=>'req', 'div'=>false)); ?>

<?php echo $form->end('Submit'); ?>
This is basically a simple form, but note the ‘class’=>’req’, we are going to use this to let javascript know wich fields to check (this might be handy if you don’t want to validate very single field in the form)
JavaScript
Now the javascript code (place it at the end of app/views/customers/add.ctp)

// Look for all req fields and put them in an array
requiredFields = document.getElementsByClassName('req');

// Loop the array and place observers on the req fields
for(i=0; i
CSS
As you can see i’m not doing much if there is an error (like not using the error message, but you could of course append a div with the error message, or alert it or… well whatever you want ;)).
To make it complete, here’s the CSS file is used.
/* ----- REQUIRED ----- */

form .req{
border-right: 2px solid red;
}

form .form-error{
display:block !important;
background-color: #FFDFDF !important;
border:1px dotted red;
}
Example
A few images with the different states: (note labels are in Dutch ;))

The field with the .req class (it creates a red right border, that disappears when the field is valid)
A valid field:

And an invalid field:

And the ajax request (from Bugzilla)

And that’s basically it. If you don’t understand everything, please read the comments or try changing stuff :)