Advanced Field Validation with AJAX

Many of you may have experience with Magento event system, so this help page will be easy to get into.

In many cases you may require instant field validation with backend queries which can be achieved with AJAX technology.

Let's create AjaxValidation extension which handles frontend field validation with AJAX and server side validation with PHP.

ONLINE DEMO: CLICK HERE.

AjaxValidation file structure

It contains 6 files in total.

app
├── code
│ └── community
│ └── Mageme
│ └── AjaxValidation
│ ├── controllers
│ │ └── ValidateController.xml
│ ├── etc
│ │ └── config.xml
│ ├── Helper
│ │ └── Data.php
│ └── Model
│ └── Observer.php
├── design
│ └── frontend
│ └── base
│ └── default
│ └── ajaxvalidation
│ └── js.phtml
└── etc
└── modules
└── Mageme_AjaxValidation.xml

app / etc / modules / Mageme_AjaxValidation.xml

This file registeres and enables extension in Magento

<?xml version="1.0"?>
<config>
<modules>
<Mageme_AjaxValidation>
<active>true</active>
<codePool>local</codePool>
</Mageme_AjaxValidation>
</modules>
</config>

app / code / local / Mageme / AjaxValidation

This folder contains main extension files.

etc / config.xml

In this file we are registering model for our extension and hook it to WebForms event "webforms_validate_post_result". The event is used to add custom server side validation.

<?xml version="1.0"?>
<config>
<modules>
<Mageme_AjaxValidation>
<version>0.1.0</version>
</Mageme_AjaxValidation>
</modules>
<frontend>
<routers>
<ajaxvalidation>
<use>standard</use>
<args>
<module>Mageme_AjaxValidation</module>
<frontName>ajaxvalidation</frontName>
</args>
</ajaxvalidation>
</routers>
</frontend>
<global>
<models>
<ajaxvalidation>
<class>Mageme_AjaxValidation_Model</class>
</ajaxvalidation>
</models>
<helpers>
<ajaxvalidation>
<class>Mageme_AjaxValidation_Helper</class>
</ajaxvalidation>
</helpers>
<events>
<webforms_validate_post_result>
<observers>
<ajaxvalidation>
<type>singleton</type>
<class>Mageme_AjaxValidation_Model_Observer</class>
<method>validate</method>
</ajaxvalidation>
</observers>
</webforms_validate_post_result>
</events>
</global>
</config>

Helper / Data.php

Here we define Helper which holds validation rules: CSS class to trigger the required field, validation algorithm and validation error text. Our validation algorithm is very basic: we search through 'one', 'two', 'three' strings array for required value.

<?php
class Mageme_AjaxValidation_Helper_Data
extends Mage_Core_Helper_Abstract
{
// define validation class which will be used to trigger the field
const CSS_CLASS = 'validate-ajax';
// set validation algorithm
public function validate($value)
{
$allowed_values = array('one', 'two', 'three');
return in_array($value, $allowed_values);
}
// set error text
public function getErrorText(){
return $this->__("Specified string was not found in the list.");
}
}

controllers / ValidateController.php

Our controller handles the AJAX request which checks the field data when client enters text.

<?php
class Mageme_AjaxValidation_ValidateController
extends Mage_Core_Controller_Front_Action
{
public function ajaxAction()
{
$success = array("validated" => false);
$value = $this->getRequest()->getParam('validate');
$success["validated"] = Mage::helper('ajaxvalidation')->validate($value);
$this->getResponse()->setBody(json_encode($success));
}
}

Model / Observer.php

Observer validates submission data on WebForms event "webforms_validate_post_result". If customer tries to hack the form and remove the validation from the HTML source of the page we validate the submission through the server event so that it doesn't come through.

<?php
class Mageme_AjaxValidation_Model_Observer
extends Mage_Core_Model_Abstract
{
public function validate(Varien_Object $observer){
/** @var VladimirPopov_WebForms_Model_Webforms $webform */
$webform = $observer->getWebform();
$validate = $observer->getValidate();
$errors = $validate->getErrors();
$postData = $webform->getPostData();
$fields_to_fieldsets = $webform->getFieldsToFieldsets();
// Iterate through all fields and find the validated one
foreach($fields_to_fieldsets as $fieldset){
foreach($fieldset['fields'] as $field){
// Check field CSS class
if(strstr($field->getCssClass(), Mageme_AjaxValidation_Helper_Data::CSS_CLASS)){
// Validate value with the helper
$status = Mage::helper('ajaxvalidation')->validate($postData[$field->getId()]);
if(!$status){
// Add error message
$errors[]=Mage::helper('ajaxvalidation')->getErrorText();
}
}
}
}
$validate->setErrors($errors);
}
}

app / design / frontend / base / default / template / ajaxvalidation / js.phtml

We store our JavaScript in the template file so it can be easily embedded through core/template block.

<style>
/* Define element styles for status indicators */
.ajax-loader, .ajax-success {
width: 16px;
height: 16px;
position: absolute;
top: 2px;
left: -20px;
}
/* Use standard images shipped with Magento */
.ajax-loader {
background-image: url('<?php echo $this->getSkinUrl('images/opc-ajax-loader.gif') ?>');
}
.ajax-success {
background-image: url('<?php echo $this->getSkinUrl('images/i_msg-success.gif') ?>');
}
</style>
<script>
// set validation flag
var validateAjax = false;
// get validation class
var validateAjaxClass = '<?php echo Mageme_AjaxValidation_Helper_Data::CSS_CLASS?>';
// add new validation rule and error message
Validation.add(validateAjaxClass, '<?php echo Mage::helper('ajaxvalidation')->getErrorText()?>', function () {
return validateAjax;
});
// add observer on field change
document.observe('dom:loaded', function () {
if ($$('.' + validateAjaxClass)[0]) {
$$('.' + validateAjaxClass).invoke('observe','change', function (event) {
validateAjax = false;
// show loader indicator
var elm = event.target.up();
elm.up().select('[class="ajax-success"]').invoke('remove');
elm.up().select('[class="ajax-loader"]').invoke('remove');
elm.setStyle({position: 'relative'});
elm.removeClassName('ajax-success');
elm.insert('<div class="ajax-loader"></div>');
var value = event.target.getValue();
// send Ajax request to validate value through server script
new Ajax.Request('<?php echo $this->getUrl('ajaxvalidation/validate/ajax')?>', {
method: 'post',
parameters: {'validate': value},
onSuccess: function (transport) {
var response = transport.responseText.evalJSON();
validateAjax = response.validated;
elm.up().select('[class="ajax-loader"]').invoke('remove');
Validation.validate(event.target);
// show success indicator
if (validateAjax)
elm.insert('<div class="ajax-success"></div>');
},
onFailure: function () {
elm.up().select('[class="ajax-loader"]').invoke('remove');
alert('Something went wrong...');
}
});
})
}
});
</script>

Form configuration

Now we need to add the JavaScript to our form. We can easily do it by adding the block to form Description with the code.

{{block type="core/template" template="ajaxvalidation/js.phtml"}}

javascript code injection

Field configuration

Now we edit the field and add validation class in Design tab: CSS classes for the Input element.

css class

That's all. You can download sources here.