-
Notifications
You must be signed in to change notification settings - Fork 7.6k
ClientServer Validation
Derek Jones edited this page Jul 5, 2012
·
13 revisions
Category:Core | Category:Core::Community | Category:Core::Validation
Implementing client-side and server-side validation usually requires a significant duplication of effort. Despite following the same logic rules, the code usually must be developed in two different environments - e.g. javascript and php.
For those who are too lazy to do the same work twice (like I am), here is a crude prototype of a validation library (subclass of built-in Validation class) that would do this work for you.
The usage is simple:
- Add a method to your controller, where you set up your validation rules.
- Call this method twice per form life-cycle: once before rendering the form (html), and again after it has been submitted.
- Set up validation:
function _setup_validation()
{
$this->load->library('validation');
$fields = array(
'email'=>'Email',
'password'=>'Password',
'password2'=>'Confirm Password',
'firstname' => 'First Name',
'lastname' => 'Last Name',
'address1'=>'Address 1',
'city'=>'City',
'zipcode'=>'Zip'
);
$rules = array(
'email'=>'required|valid_email',
'password'=>'min_length[5]',
'password2'=>'required|matches[password]',
'firstname' => 'required',
'lastname' => 'required',
'address1'=>'required',
'city'=>'min_length[2]',
'zipcode'=>'exact_length[5]'
);
$this->validation->set_fields($fields);
$this->validation->set_rules($rules);
}
- When rendering the form, define your validation rules and then call special function from our Validation (sub)class to generate a corresponding js script:
function index()
{
$this->_setup_validation();
$data['javascript'] = $this->validation->javascript();
$this->load->view('register', $data);
}
- In your view, use 'javascript' variable to inject the contents of our validation (js) script:
<html>
<head>
<title>Account Registration</title>
<?= @$javascript ?>
</head>
...
- Once the form has been submitted, process it as usual:
function submit()
{
$this->_setup_validation();
if ($this->validation->run() == false)
return $this->index();
$this->load->view('sucess');
}
Here's MY_Validation.php in its entirety:
<?php if (!defined('BASEPATH')) exit('No direct script access allowed');
// ------------------------------------------------------------------------
/**
* Validation Class extension
*
* @package CodeIgniter
* @subpackage Libraries
* @category Validation
* @author AK
* @link http://codeigniter.com/user_guide/libraries/validation.html
*/
/**
* Set Fields
*
* This function takes an array of field names as input
* and generates class variables with the same name, which will
* either be blank or contain the $_POST value corresponding to it
*
* @access public
* @param string
* @param string
* @return void
*/
class MY_Validation extends CI_Validation {
function MY_Validation()
{
parent::CI_Validation();
}
/**
* JavaScript
*
* This function provides default implementation of the built-in CI validation rules in javascript.
* The function generates a client-side js script, complete with <script>...</script> html tags,
* suitable for inclusion in the document header. Additionally, custom rules can be added by
* defining global js functions, or extending Validation js object.
*
* @access public
* @param string - custom error message (optional)
* @param string - name of a js error callback function (optional)
* @return string - js
*/
function javascript($alert_msg='', $alert_func='null')
{
if(!$this->_fields || !$this->_rules)
return '<script type="text/javascript">function validation_run(f) {}</script>';
// client-side javascript implementation of CI built-in validation rules.
$script = '
<script type="text/javascript">
String.prototype.trim = function() { return this.replace(/^\s\s*/, "").replace(/\s\s*$/, ""); }
var Validator = function(f) { this.form = f; }
Validator.prototype.required = function(str) { return str.search(/\S/) > -1; }
Validator.prototype.matches = function(str, field) { return (str == this.form.elements[field].value); } // FIX! change "field" from input name to input ref?
Validator.prototype.max_length = function(str, val) { return (str.length <= val); }
Validator.prototype.min_length = function(str, val) { return (str.length >= val); }
Validator.prototype.exact_length = function(str, val) { return (str.length == val); }
Validator.prototype.valid_email = function(str) { return str.search(/^([\w\+\-]+)(\.[\w\+\-]+)*@([a-z\d\-]+\.)+[a-z]{2,6}$/i) > -1; }
Validator.prototype.valid_ip = function(ip) { var segments = ip.split("."); for (var i in segs) if(segs[i].length>3 || segs[i]>255 || segs[i].search(/\D/)>-1) return false; return true; }
Validator.prototype.alpha = function(str) { return str.search(/[^a-z]/i) == -1; }
Validator.prototype.alpha_numeric = function(str) { return str.search(/[^a-z0-9]/i) == -1; }
Validator.prototype.alpha_dash = function(str) { return str.search(/[^\w-]/i) == -1; }
Validator.prototype.numeric = function(str) { return ! isNaN(str); }
Validator.prototype.integer = function(str) { return ! (isNaN(str) || str.indexOf(".") > -1); }
Validator.prototype.valid_base64 = function(str) { return str.search(/[^a-zA-Z0-9\/\+=]/) == -1; }
Validator.prototype.validate = function (rules, callback) { try {
if (!rules.length) return true;
var res, errors=[];
for (var i in rules) {
var item = rules[i];
var field = this.form.elements[item.input];
var rule_list = item.rule.split("|");
for (var r in rule_list) {
var re = /(callback_|validate_)?(\w+)(?:\[(.+)\])?/i.exec(rule_list[r]);
var func = re[2];
//if (!re[1]) re[1] = "";
if (!this[func]) {
try { func = eval(func); } catch (e2) { }
res = (typeof(func) == "function") ? func(field.value, re[3]) : false;
} else {
res = this[func](field.value, re[3]);
}
}
if (!res && item.msg) {
errors.push([item.msg, item.input]);
}
} } catch (e) { alert(e); }
if (errors.length) {
// show errors
return callback ? callback(errors) : display_alert(errors);
}
return true;
}
';
// default alert message
if ($alert_msg == '')
$alert_msg = 'Please fix the following errors:';
// default implementation of the validation action
$script .= '
function display_alert(errors) {
var str = "";
for (var i in errors)
str += "\n- " + errors[i][0];
alert("'. addslashes($alert_msg) .'" + str);
return false;
}
';
// Load the language file containing error messages
$this->CI->lang->load('validation');
$params = null;
foreach ($this->_rules as $field => $rules)
{
//Explode out the rules!
$ex = explode('|', $rules);
$messages = array();
foreach ($ex as $rule)
{
$param = FALSE;
if (preg_match("/(.*?)\[(.*?)\]/", $rule, $match))
{
$rule = $match[1];
$param = $match[2];
}
if ( ! isset($this->_error_messages[$rule]))
{
if (FALSE === ($line = $this->CI->lang->line($rule)))
{
$line = 'Unable to access an error message corresponding to your field name.';
}
}
else
{
$line = $this->_error_messages[$rule];
}
// Build the error message
$mfield = ( ! isset($this->_fields[$field])) ? $field : $this->_fields[$field];
$mparam = ( ! isset($this->_fields[$param])) ? $param : $this->_fields[$param];
$messages[] = sprintf($line, $mfield, $mparam);
}
$params[] = '{input:"'.$field.'",rule:"'.$rules.'",name:"'
.( isset($this->_fields[$field]) ? addslashes($this->_fields[$field]) : $field ).'",msg:"'.join(' ', $messages).'"}';
}
$script .= "\nfunction validation_run(f) {\n\tvar rules = [\n\t\t" . join(",\n\t\t", $params) . "\n\t];\n\treturn new Validator(f).validate(rules,". $alert_func .");\n}\n</script>";
return $script;
}
}
?>