Creating custom validation rules
Webform
Let's say you have written a custom PHP validator for the Webform Validation module that looks like this (assuming your custom module is named potpourri):
potpourri.module
/**
* Implementation of hook_webform_validation_validators().
*/
function potpourri_webform_validation_validators() {
return array(
'phonefield' => array(
'name' => "Phonefield",
'component_types' => array(
'textfield',
),
'description' => 'Check phone number'
)
);
}
/**
* Implementation of hook_webform_validation_validate().
*/
function potpourri_webform_validation_validate($validator_name, $items, $components, $rule) {
if ($items) {
$errors = array();
switch ($validator_name) {
case 'phonefield':
foreach ($items as $key => $val) {
if ($val && (!isTelefon($val))) {
$errors[$key] = t("%value is not a telephone.", array('%value' => $val));
}
}
return $errors;
break;
}
}
}
/**
* Validation callback function.
*/
function isTelefon($text) {
return preg_match('/^(\+){0,1}(\([0-9]+\))?[0-9_\-\/]{6,}$/', str_replace(' ', '', $text));
}
This would be enough if you want just server side validation. If you want to support Clientside Validation there are a few more things you need to do.
First, you need to implement hook_clientside_validation_rule_alter(). In this example it would look like this:
potpourri.module
/**
* Implementation of hook_clientside_validation_rule_alter().
*/
function potpourri_clientside_validation_rule_alter (&$js_rules, $element, $context) {
switch ($context['type']) {
case 'webform':
if ($context['rule']['validator'] == 'phonefield') {
drupal_add_js(drupal_get_path('module', 'potpourri') . '/phone_validator.js');
$title = variable_get('clientside_validation_prefix', '') . $element['element_title'] . variable_get('clientside_validation_suffix', '');
$js_rules[$element['element_name']]['phoneField'] = TRUE;
$js_rules[$element['element_name']]['messages']['phoneField'] = theme('clientside_error', array('message' => 'Field !name must be a valid phone number.', 'placeholders' => array('!name' => $title)));
}
break;
}
}
Finally, you need to create the phone_validator.js file in the map of you module. In this file you have to write the javascript validation:
phone_validator.js for Drupal 7
//jQuery wrapper
(function ($) {
//Define a Drupal behaviour with a custom name
Drupal.behaviors.potpourriAddPhoneValidator = {
attach: function (context) {
//Add an eventlistener to the document reacting on the
//'clientsideValidationAddCustomRules' event.
$(document).bind('clientsideValidationAddCustomRules', function(event){
//Add your custom method with the 'addMethod' function of jQuery.validator
//http://docs.jquery.com/Plugins/Validation/Validator/addMethod#namemethodmessage
jQuery.validator.addMethod("phoneField", function(value, element, param) {
//return true if valid, false if invalid
var regexp = /^(\+){0,1}(\([0-9]+\))?[0-9_\-\/]{6,}$/
return regexp.test(value.replace(' ', ''));
//Enter a default error message.
}, jQuery.format('Value must a valid phone number'));
});
}
}
})(jQuery);
phone_validator.js for Drupal 6
//Define a Drupal behaviour with a custom name
Drupal.behaviors.potpourriAddPhoneValidator = {
attach: function (context) {
//Add an eventlistener to the document reacting on the
//'clientsideValidationAddCustomRules' event.
$(document).bind('clientsideValidationAddCustomRules', function(event){
//Add your custom method with the 'addMethod' function of jQuery.validator
//http://docs.jquery.com/Plugins/Validation/Validator/addMethod#namemethodmessage
jQuery.validator.addMethod("phoneField", function(value, element, param) {
//return true if valid, false if invalid
var regexp = /^(\+){0,1}(\([0-9]+\))?[0-9_\-\/]{6,}$/
return regexp.test(value.replace(/\s/g, ''));
//Enter a default error message.
}, jQuery.format('Value must a valid phone number'));
});
}
}
Note that the first parameter to the addMethod function is the same as the array key used in potpourri_clientside_validation_rule_alter.
And that's how you create a custom validator for Webform Validation!
Form API Validation
For the Form API Validation module it's basically the same principle.
So, again, let's say you have written a custom PHP validator for the Form API Validation module that looks like this (again assuming your custom module is named potpourri):
potpourri.module
/**
* Implements hook_fapi_validation_rules().
*/
function potpourri_fapi_validation_rules() {
return array(
'check_empty' => array(
'callback' => 'potpourri_validation_check_empty',
'error_msg' => t('%field cannot be empty'),
),
);
}
/**
* Validation callback function.
*/
function potpourri_validation_check_empty($value) {
return !empty($value);
}
Now, if you want to support Clientside Validation you need to implement hook_clientside_validation_rule_alter(). In this example it would look like this:
/**
* Implements hook_clientside_validation_rule_alter()
*/
function potpourri_clientside_validation_rule_alter(&$js_rules, $element, $context) {
switch ($context['type']) {
case 'fapi':
if ($context['rule']['callback'] == 'potpourri_validation_check_empty') {
_potpourri_set_not_empty($element['#name'], $element['#title'], $js_rules);
}
break;
}
}
function _potpourri_set_not_empty($name, $title, &$js_rules){
drupal_add_js(drupal_get_path('module', 'potpourri') . '/potpourri_validation.js');
$title = variable_get('clientside_validation_prefix', '') . $title . variable_get('clientside_validation_suffix', '');
$js_rules[$name]['notEmpty'] = TRUE;
$js_rules[$name]['messages']['notEmpty'] = t('Must Enter a value in !field.', array('!field' => $title));
}
Finally, you need to create the potpourri_validation.js file in the map of you module. In this file you have to write the javascript validation:
potpourri_validation.js for Drupal 7
//jQuery wrapper
(function ($) {
//Define a Drupal behaviour with a custom name
Drupal.behaviors.potpourriAddNotEmptyValidator = {
attach: function (context) {
//Add an eventlistener to the document reacting on the
//'clientsideValidationAddCustomRules' event.
$(document).bind('clientsideValidationAddCustomRules', function(event){
//Add your custom method with the 'addMethod' function of jQuery.validator
//http://docs.jquery.com/Plugins/Validation/Validator/addMethod#namemethodmessage
jQuery.validator.addMethod("notEmpty", function(value, element, param) {
return value !== '';
}, jQuery.format('Field can not be empty'));
});
}
}
})(jQuery);
potpourri_validation.js for Drupal 6
//Define a Drupal behaviour with a custom name
Drupal.behaviors.potpourriAddNotEmptyValidator = function (context) {
//Add an eventlistener to the document reacting on the
//'clientsideValidationAddCustomRules' event.
$(document).bind('clientsideValidationAddCustomRules', function(event){
//Add your custom method with the 'addMethod' function of jQuery.validator
//http://docs.jquery.com/Plugins/Validation/Validator/addMethod#namemethodmessage
jQuery.validator.addMethod("notEmpty", function(value, element, param) {
return value !== '';
}, jQuery.format('Field can not be empty'));
});
}
And that's how you create a custom validator for Form API Validation!
Validation for #element_validate
If you have written a custom PHP validator for Form API's #element_validate
that looks like this (assuming your custom module is named potpourri):
potpourri.module
/**
* Implements hook_menu().
*/
function potpourri_menu() {
return array(
'potpourri_form' => array(
'title' => t('Test Clientside Validation'),
'page callback' => 'drupal_get_form',
'page arguments' => array('potpourri_form'),
'access arguments' => array('access content'),
),
);
}
/**
* Form callback.
*/
function potpourri_form() {
$form = array();
$form['container'] = array(
'#type' => 'item',
'#title' => t('Container'),
'#tree' => TRUE,
'#element_validate' => array('_container_validate'),
'#prefix' => '<div class="my-container">',
'#suffix' => '</div>',
);
$form['container']['textfield_one'] = array(
'#type' => 'textfield',
'#title' => t('Textfield 1'),
'#tree' => TRUE,
);
$form['container']['textfield_two'] = array(
'#type' => 'textfield',
'#title' => t('Textfield 2'),
'#tree' => TRUE,
);
$form['submit'] = array(
'#type' => 'submit',
'#value' => t('Submit'),
);
return $form;
}
/**
* Validation callback
*/
function _container_validate($element, &$form_state, $form) {
if ($form_state['values']['container']['textfield_one'] == $form_state['values']['container']['textfield_two']) {
form_set_error('container', t("The two fields cannot have the same value."));
}
}
This would be enough if you want just server side validation. If you want to support Clientside Validation there's one more thing you need to do.
You need to implement hook_clientside_validation_rule_alter(). In this example it would look like this:
potpourri.module
/**
* Implements hook_clientside_validation_rule_alter().
*/
function potpourri_clientside_validation_rule_alter(&$js_rules, $element, $context) {
switch($context['type']) {
case 'element_validate':
if (in_array('_container_validate', $context['functions'])) {
_clientside_validation_set_not_equal(
$element['textfield_one']['#name'],
$element['textfield_one']['#title'],
array(
'form_key' => $element['textfield_two']['#name'],
'name' => $element['textfield_two']['#title']
),
$js_rules,
t("The two fields cannot have the same value")
);
}
break;
}
}
The function _clientside_validation_set_not_equal
is provided in clientside_validation.module. If you want to write your own custom validators you can look at the examples above.
And that's how you create a custom validator for #element_validate
!
Validation for #validate
If you have written a custom PHP validator for Form API's #validate
that looks like this (assuming your custom module is named potpourri):
potpourri.module
/**
* Implements hook_menu().
*/
function potpourri_menu() {
return array(
'potpourri_form' => array(
'title' => t('Test Clientside Validation'),
'page callback' => 'drupal_get_form',
'page arguments' => array('potpourri_form'),
'access arguments' => array('access content'),
),
);
}
/**
* Form callback.
*/
function potpourri_form() {
$form = array();
$form['container'] = array(
'#type' => 'item',
'#title' => t('Container'),
'#tree' => TRUE,
'#prefix' => '<div class="my-container">',
'#suffix' => '</div>',
);
$form['container']['textfield_one'] = array(
'#type' => 'textfield',
'#title' => t('Textfield 1'),
'#tree' => TRUE,
);
$form['container']['textfield_two'] = array(
'#type' => 'textfield',
'#title' => t('Textfield 2'),
'#tree' => TRUE,
);
$form['submit'] = array(
'#type' => 'submit',
'#value' => t('Submit'),
);
$form['#validate'][] = 'my_custom_validation';
return $form;
}
/**
* Validation callback
*/
function my_custom_validation($form, &$form_state, $form) {
if ($form_state['values']['container']['textfield_one'] == $form_state['values']['container']['textfield_two']) {
form_set_error('container', t("The two fields cannot have the same value."));
}
}
This would be enough if you want just server side validation. If you want to support Clientside Validation there's one more thing you need to do.
You need to implement hook_clientside_validation_rule_alter(). In this example it would look like this:
potpourri.module
/**
* Implements hook_clientside_validation_rule_alter().
*/
function potpourri_clientside_validation_rule_alter(&$js_rules, $element, $context) {
switch($context['type']) {
case 'form_validate':
if (in_array('my_custom_validation', $context['functions'])) {
_clientside_validation_set_not_equal(
$element['container']['textfield_one']['#name'],
$element['container']['textfield_one']['#title'],
array(
'form_key' => $element['container']['textfield_two']['#name'],
'name' => $element['container']['textfield_two']['#title']
),
$js_rules,
t("The two fields cannot have the same value")
);
}
break;
}
}
The function _clientside_validation_set_not_equal
is provided in clientside_validation.module. If you want to write your own custom validators you can look at the examples above.
And that's how you create a custom validator for #validate
!
Field Validation (Drupal 7 only)
If you have written a custom PHP validator for the Field Validation module that looks like this (assuming your custom module is named potpourri):
potpourri.module
/**
* Implements hook_field_validation_validators().
*/
function potpourri_field_validation_validators() {
return array(
'must_be_empty' => array(
'name' => "Must be empty",
'field_types' => array(
'textfield',
),
'custom_error' => TRUE,
'description' => t('Verifies that a specified textfield remains empty - Recommended use case: used as an anti-spam measure by hiding the element with CSS'),
),
);
}
/**
* Implementation of hook_webform_validation_validate().
*/
/**
* Implements hook_field_validation_validate().
*/
function field_validation_field_validation_validate($validator_name, $rule, $entity, $langcode, $items, &$errors) {
if (!empty($items)) {
switch ($validator_name) {
case "must_be_empty":
foreach ($items as $delta => $item) {
if ($item[$rule['col']]) {
$errors[$rule['field_name']][$langcode][$delta][] = array(
'error' => 'must_be_empty_'.$rule['ruleid'],
'message' => t($rule['error_message']),
);
}
}
break;
}
}
}
This would be enough if you want just server side validation. If you want to support Clientside Validation there are a few more things you need to do.
First, you need to implement hook_clientside_validation_rule_alter(). In this example it would look like this:
potpourri.module
/**
* Implementation of hook_clientside_validation_rule_alter().
*/
function potpourri_clientside_validation_rule_alter (&$js_rules, $element, $context) {
switch ($context['type']) {
case 'field_validation':
if ($context['rule']['validator'] == 'must_be_empty') {
drupal_add_js(drupal_get_path('module', 'potpourri') . '/must_be_empty_validator.js');
$title = variable_get('clientside_validation_prefix', '') . $element['#title'] . variable_get('clientside_validation_suffix', '');
$js_rules[$element['element_name']]['mustBeEmpty'] = TRUE;
$js_rules[$element['element_name']]['messages']['mustBeEmpty'] = theme('clientside_error', array('message' => 'Field !name must be empty.', 'placeholders' => array('!name' => $title)));
}
break;
}
}
Finally, you need to create the must_be_empty_validator.js file in the map of you module. In this file you have to write the javascript validation:
must_be_empty_validator.js
//jQuery wrapper
(function ($) {
//Define a Drupal behaviour with a custom name
Drupal.behaviors.potpourriAddMustBeEmptyValidator = {
attach: function (context) {
//Add an eventlistener to the document reacting on the
//'clientsideValidationAddCustomRules' event.
$(document).bind('clientsideValidationAddCustomRules', function(event){
//Add your custom method with the 'addMethod' function of jQuery.validator
//http://docs.jquery.com/Plugins/Validation/Validator/addMethod#namemethodmessage
jQuery.validator.addMethod("mustBeEmpty", function(value, element, param) {
//return true if valid, false if invalid
return value === '';
//Enter a default error message.
}, jQuery.format('Value must be empty'));
});
}
}
})(jQuery);
And that's how you create a custom validator for Field Validation!
Comments
Side note
Hi,
Thank you for these great examples, it helped me a lot with a case I was working on.
I want to comment about an issue I had when testing your code. For "potpourri_validation.js for Drupal 6" section I found one error that I think is related with the way that the attach function is declared. In order to solve the problem I had to change:
Greetings.
I also found this to be the
I also found this to be the case, so I updated it.
Hello
hello , i'm not able to get it working
Here is my code inside a custom module :
and the javascript
/
What am i doing wrong
Thank you for your help