Rails-like Data Validation

by chess64
Validate your data like in rails: http://rails.rubyonrails.com/classes/ActiveRecord/Validations/ClassMethods.html
Now you can validate your data (almost) like in rails (see http://rails.rubyonrails.com/classes/ActiveRecord/Validations/ClassMethods.html)!

Note: this has not been fully tested yet, so please submit bugs and suggestions...

Example Usage


Functions and parameters are almost exactly the same as the rails functions and parameters. As of right now, there is no validates_agreement_of() or validates_confirmation_of().

In your users model, you might have:

Model Class:

<?php 
class User extends AppModel {
    function 
validates() {
        
$this->setAction();
        
#always validate presence of username
        
$this->validates_presence_of('username');
        
#validate uniqueness of username when creating a new user
        
$this->validates_uniqueness_of('username',array('on'=>'create'));
        
#validate length of username (minimum)
        
$this->validates_length_of('username',array('min'=>3));
        
#validate length of username (maximum)
        
$this->validates_length_of('username',array('max'=>50));
        
#validate presence of password
        
$this->validates_presence_of('password');
        
#validate presence of email
        
$this->validates_presence_of('email');
        
#validate uniqueness of email when creating a new user
        
$this->validates_uniqueness_of('email',array('on'=>'create'));
        
#validate format of email
        
$this->validates_format_of('email',VALID_EMAIL);

        
#if there were errors, return false
        
$errors $this->invalidFields();
        return (
count($errors) == 0);
    }
}
?>

The Code


Place the following code in app/app_model.php:

Model Class:

<?php 
class AppModel extends Model {
        var 
$action;
        function 
setAction() {
                if (empty(
$this->id)) {
                        
$this->action 'create';
                } else {
                        
$this->action 'update';
                }
                return 
true;
        }
        function 
invalidFields($data = array()) {
                if ( empty(
$data) ) {
                        
$data $this->data;
                }
                if ( !
$this->beforeValidate() ) {
                        return 
$this->validationErrors;
                }
                return 
$this->validationErrors;
        }

        function 
validates_presence_of($fieldName,$options=array()) {
                if ( !isset(
$options['message']) ) {
                        
$options['message'] = Inflector::humanize($fieldName) . ' is required.';
                }
                if ( !isset(
$options['on']) ) {
                        
$options['on'] = 'save';
                }
                if ( ((
$options['on'] == 'save') || ($options['on'] == $this->action)) ) {
                        if ( empty(
$this->data[$this->name][$fieldName]) ) {
                                
$this->validationErrors[$fieldName] = $options['message'];
                        }
                }
        }
        function 
validates_exclusion_of($fieldName,$options=array()) {
                
$fieldValue $this->data[$this->name][$fieldName];
                if ( @
$options['allow_null'] && ($fieldValue == null) ) {
                        return 
true;
                }
                if ( !isset(
$options['in']) ) {
                        
$options['in'] = array();
                }
                if ( !isset(
$options['message']) ) {
                        
$options['message'] = Inflector::humanize($fieldName) . ' should be one of the following: ' join(',',$options['in']) . '.';
                }
                if ( !isset(
$options['on']) ) {
                        
$options['on'] = 'save';
                }
                if ( ((
$options['on'] == 'save') || ($options['on'] == $this->action)) ) {
                        if ( 
in_array($fieldValue,$options['in']) ) {
                                
$this->validationErrors[$fieldName] = $options['message'];
                        }
                }
        }
        function 
validates_format_of($fieldName,$options=array()) {
                
$fieldValue $this->data[$this->name][$fieldName]; 
                if ( @
$options['allow_null'] && ($fieldValue == null) ) {
                        return 
true;
                }
                if ( !isset(
$options['message']) ) {
                        
$options['message'] = Inflector::humanize($fieldName) . ' has an invalid format.';
                }
                if ( !isset(
$options['on']) ) {
                        
$options['on'] = 'save';
                }
                if ( !isset(
$options['with']) ) {
                        
$options['with'] = '//';
                }
                if ( ((
$options['on'] == 'save') || ($options['on'] == $this->action)) ) {
                        if ( !
preg_match($options['with'],$fieldValue) ) {
                                
$this->validationErrors[$fieldName] = $options['message'];
                        }
                }
        }
        function 
validates_inclusion_of($fieldName,$options=array()) {
                
$fieldValue $this->data[$this->name][$fieldName];
                if ( @
$options['allow_null'] && ($fieldValue == null) ) {
                        return 
true;
                }
                if ( !isset(
$options['in']) ) {
                        
$options['in'] = array();
                }
                if ( !isset(
$options['message']) ) {
                        
$options['message'] = Inflector::humanize($fieldName) . ' should be one of the following: ' join(',',$options['in']) . '.';
                }
                if ( !isset(
$options['on']) ) {
                        
$options['on'] = 'save';
                }
                if ( ((
$options['on'] == 'save') || ($options['on'] == $this->action)) ) {
                        if ( !
in_array($fieldValue,$options['in']) ) {
                                
$this->validationErrors[$fieldName] = $options['message'];
                        }
                }
        }
        function 
validates_length_of($fieldName,$options=array()) {
                
$fieldValue $this->data[$this->name][$fieldName];
                if ( @
$options['allow_null'] && ($fieldValue == null) ) {
                        return 
true;
                }
                if ( !isset(
$options['message']) ) {
                        
$options['message'] = Inflector::humanize($fieldName) . ' has the wrong length.';
                }
                if ( !isset(
$options['on']) ) {
                        
$options['on'] = 'save';
                }
                if ( ((
$options['on'] == 'save') || ($options['on'] == $this->action)) ) {
                        if ( isset(
$options['max']) ) {
                                if ( 
strlen($fieldValue) > $options['max'] ) {
                                        
$this->validationErrors[$fieldName] = $options['message'];
                                }
                        } elseif ( isset(
$options['min']) ) {
                                if ( 
strlen($fieldValue) < $options['min'] ) {
                                        
$this->validationErrors[$fieldName] = $options['message'];
                                }
                        } elseif ( isset(
$options['in']) ) {
                                if ( !
in_array($fieldValue,$options['in']) ) {
                                        
$this->validationErrors[$fieldName] = $options['message'];
                                }
                        } elseif ( isset(
$options['is']) ) {
                                if ( 
$fieldValue != $options['is'] ) {
                                        
$this->validationErrors[$fieldName] = $options['message'];
                                }
                        }
                }
        }
        function 
validates_numericality_of($fieldName,$options=array()) {
                
$fieldValue $this->data[$this->name][$fieldName];
                if ( @
$options['allow_null'] && ($fieldValue == null) ) {
                        return 
true;
                }
                if ( !isset(
$options['only_integer']) ) {
                        
$options['only_integer'] = false;
                }
                if ( !isset(
$options['message']) ) {
                        if ( 
$options['only_integer'] ) {
                                
$options['message'] = Inflector::humanize($fieldName) . ' should be an integer.';
                        } else {
                                
$options['message'] = Inflector::humanize($fieldName) . ' should be a number.';
                        }
                }
                if ( !isset(
$options['on']) ) {
                        
$options['on'] = 'save';
                }
                if ( ((
$options['on'] == 'save') || ($options['on'] == $this->action)) ) {
                        if (
                                !
is_numeric($fieldValue)
                                || ( 
$options['only_integer'] && !is_int($fieldValue) )
                        ) {
                                
$this->validationErrors[$fieldName] = $options['message'];
                        }
                }
        }
        function 
validates_uniqueness_of($fieldName,$options=array()) {
                
$fieldValue $this->data[$this->name][$fieldName];
                if ( @
$options['allow_null'] && ($fieldValue == null) ) {
                        return 
true;
                }
                if ( !isset(
$options['message']) ) {
                        
$options['message'] = Inflector::humanize($fieldName) . ' is already taken.';
                }
                if ( !isset(
$options['on']) ) {
                        
$options['on'] = 'save';
                }
                if ( ((
$options['on'] == 'save') || ($options['on'] == $this->action)) ) {
                        if ( 
$this->hasAny(array("{$this->name}.{$fieldName}=> $fieldValue)) ) {
                                
$this->validationErrors[$fieldName] = $options['message'];
                        }
                }
        }
}
?>

Place the following code in app/views/helpers/error.php:

Helper Class:

<?php 
class ErrorHelper extends Helper {
    function 
forField($field) {
        list(
$model,$fieldName) = explode('/',$field);
        if ( isset(
$this->validationErrors($model,$fieldName) ) {
            return 
'<div class="error">' $this->validationErrors[$model][$fieldName] . '</div>';
        } else {
            return 
'';
        }
    }
}
?>

In your controller, put

Controller Class:

<?php 
class BananasController extends AppController {
    
/* ... */
    
var $helpers = array('Html','Error');
    
/* ... */
}
?>

In your views, put

View Template:


<label for="quantity">Quantity:
    <input type="text" name="data[Banana][quantity]" id="quantity" />
</label><?php print $error->forField('Banana/quantity'); ?>

In your model, put:

Model Class:

<?php 
class Banana extends AppModel {
    function 
validates() {
        
#make sure quantity is an integer
        
$this->validates_numericality_of('quantity',array('only_integer'=>true));

        
#if there were errors, return false
        
$errors $this->invalidFields();
        return (
count($errors) == 0);
    }
}
?>

Please comment!

Report

More on Models

Tags

Advertising

Comments

  • coeus posted on 10/23/07 04:19:36 PM
    I was wondering if anyone has gotten this to worked with $this->Model->validates($data);

    It works well with $this->Model->save($data) but for some reason it doesn't work with $this->Model->validates($data)
  • jpgeek posted on 10/21/07 02:41:17 PM

    Hi Chess64,

    first of all, I'd like to say thanks for submitting this article. After coding in Rails for quite sometime, I got used to certain things there and coding in other ways seems a bit difficult. So again, thanks.

    I needed a function that worked like validates_confirmation_of. I made one based on the templates of the functions in your code. The function looks as follows:


    validates_confirmation_of function


        /**
         * function to run a validates_confirmation_of on CakePHP model. This is an addition to
         * rails-like-validations for CakePHP. Originally inpired by Adeel Khan. More details
         * about setting it up here: http://bakery.cakephp.org/articles/view/rails-like-data-validation
         *
         * @author Jean-Paul Hounkanrin
         * @version 21-October-2007, 8:55 Pm.
         */
        public function validates_confirmation_of($fieldName, $options=array()) {
            $varToConfirm = $fieldName . "_confirmation";
            $varToConfirmValue = null;
            $fieldValue = $this->data[$this->name][$fieldName];
            
            if(!isset($options['on'])) {
                $options['on'] = 'save';
            }
                
            if(isset($this->data[$this->name][$varToConfirm])) {
                $varToConfirmValue = $this->data[$this->name][$varToConfirm];        
            }
            
            if(!isset($options['message'])) {
                $options['message'] = Inflector::humanize($varToConfirm) . ' and ' . Inflector::humanize($fieldName) . ' are not equivalent.';
            }        

            if ((($options['on'] == 'save') || ($options['on'] == $this->action))) {
                if ($fieldValue !== $varToConfirmValue) {
                    $this->validationErrors[$fieldName] = $options['message'];
                }
            }
        }


    You might want to test the code above. I've written a small test class for it. The code is as follows:


    Test validates_confirmation_of function


    <?php
    require_once('inflector.php');

    $test = new TestConfirmValidation();
    $test->testValidatesConfirmationOf();

    class 
    TestConfirmValidation {

        var 
    $data null;
        var 
    $validationErrors null;
        var 
    $name null;

        
    /** test class constructor */
        
    public function __construct() {
            
    $this->name 'TestConfirmValidation';
            
            
    $this->validationErrors = array(
                
    'password' => 'valid',
                
    'other_var' => 'valid' // this should contain error msg after validation call
            
    );
            
            
    $this->data = array(
                
    'TestConfirmValidation' => array(
                    
    // supposed to pass test
                    
    'password' => 'c0nfirm_pa55w0rd',
                    
    'password_confirmation' => 'c0nfirm_pa55w0rd',
                
                    
    // supposed to fail test
                    
    'other_var' => 'hell0',
                    
    'other_var_confirmation' => 'hello'
                
    )
            );
        }

        
    /** validates the validates_confirmation_of(...) function */
        
    public function testValidatesConfirmationOf() {
            
    $this->validates_confirmation_of('password');
            
    $this->validates_confirmation_of('other_var');
            
            
    // This should show that password passed the validation while other_var failed it.
            // As follows: Array ( [password] => valid [other_var] => Other Var Confirmation and Other Var are not equivalent. )
            
    print_r($this->validationErrors);
        }

        
    /**
         * function to run a validates_confirmation_of on CakePHP model. This is an addition to
         * rails-like-validations for CakePHP. Originally inpired by Adeel Khan. More details
         * about setting it up here: http://bakery.cakephp.org/articles/view/rails-like-data-validation
         *
         * @author Jean-Paul Hounkanrin
         * @version 21-October-2007, 8:55 Pm.
         */
        
    public function validates_confirmation_of($fieldName$options=array()) {
            
    $varToConfirm $fieldName "_confirmation";
            
    $varToConfirmValue null;
            
    $fieldValue $this->data[$this->name][$fieldName];
            
            if(!isset(
    $options['on'])) {
                
    $options['on'] = 'save';
            }
                
            if(isset(
    $this->data[$this->name][$varToConfirm])) {
                
    $varToConfirmValue $this->data[$this->name][$varToConfirm];        
            }
            
            if(!isset(
    $options['message'])) {
                
    $options['message'] = Inflector::humanize($varToConfirm) . ' and ' Inflector::humanize($fieldName) . ' are not equivalent.';
            }        

            if (((
    $options['on'] == 'save') || ($options['on'] == $this->action))) {
                if (
    $fieldValue !== $varToConfirmValue) {
                    
    $this->validationErrors[$fieldName] = $options['message'];
                }
            }
        }
    }
    ?>

    I hope this helps someone out there.


    Cheers,

    Jean-Paul H.
  • roganovica posted on 07/23/07 01:56:21 AM
    in the first paragraph when you check e-mail
    $this->validates_format_of('email',array('with'=>VALID_EMAIL));
    instead
    $this->validates_format_of('email',array(VALID_EMAIL);
    this is a small error.
    Thank you for write this validation, it's very useful

  • _john posted on 07/22/07 08:14:43 AM
    Im trying to use this rails-like-validation but i have problem with the helper.

    Helper Class:

    <?php 
    class ErrorHelper extends Helper {
        function 
    forField($field) {
            list(
    $model,$fieldName) = explode('/',$field);
            if ( isset(
    $this->validationErrors[$model][$fieldName]) ) ) {
                return 
    '<div class="error">' $this->validationErrors[$model][$fieldName] . '</div>';
            } else {
                return 
    '';
            }
        }
    }
    ?>

    the statement $this->validationErrors[$model][$fieldName] does not returning any result. That's why the error message is not displaying.

    can anyone help me? i switch back to version 1.1.15.XXXX. would that be an issue?
  • cecilio posted on 07/19/07 11:18:42 PM
    btw, im using cakephp version 1.1.16
  • cecilio posted on 07/19/07 11:16:56 PM
    how can i execute the validates() function in my model? it seems that cake isn't executing that function.
  • coeus posted on 07/11/07 08:06:26 PM
    I like it. Clean and simple to use. Nice job!
  • noWookies posted on 06/18/07 10:13:34 AM
    my error, sorry, nevermind.

    This works great !

    PS: It would be good if one could delete one's comments, at least if they are the last reply.
  • pluriels posted on 06/05/07 12:31:04 PM
    Thanx chess64 for your Rails-like Data Validation
    I had to correct the Error Helper, after a copy/paste from screen...
    Maybe that was because i used 1.1.15.XXX version.

    <?php 
    class ErrorHelper extends Helper {
        function 
    forField($field) {
            list(
    $model,$fieldName) = explode('/',$field);
            if ( isset(
    $this->validationErrors[$model][$fieldName]) ) ) {
                return 
    '<div class="error">' $this->validationErrors[$model][$fieldName] . '</div>';
            } else {
                return 
    '';
            }
        }
    }
    ?> 
login to post a comment.