Search code examples
phplaravellaravel-4

Dynamically get a list of Laravel Validation Rules?


Is there any way to dynamically retrieve a list of "legal" validation rules? I'm trying to have my models self-validate their own validation rule string, to make sure it is accurate. i.e. to make sure someone didn't type "requierd".

I see getRules() at http://laravel.com/api/class-Illuminate.Validation.Validator.html, but that only returns ruled used within the validation, not a list of all known rules.


Solution

  • There's no official API to do this, so you'll need to use reflection. If you look at the implementation of the validate method, you'll see that rules are simply methods on the validate object (that's returned from the static call to make)

    #File: vendor/laravel/framework/src/Illuminate/Validation/Validator.php
    protected function validate($attribute, $rule)
    {
        //...
        $method = "validate{$rule}";
    
        if ($validatable && ! $this->$method($attribute, $value, $parameters, $this))
        {
            $this->addFailure($attribute, $rule, $parameters);
        }
        //...
    }
    

    This means we can use reflection to grab a list of validate rules. Also, the method names are camel case with a leading capital letter ("studly case" in laravel speak) so we'll need to lower-case/underscore them ("snake case" in laravel speak) to get the actual validation rule name. We'll also identify which rules have : parameters. Unfortunately, there's no way to derive what each rule expects the parameter to be.

    $validator = Validator::make(array(), array());
    
    //
    $r = new ReflectionClass($validator);
    $methods = $r->getMethods();  
    
    //filter down to just the rules
    $methods = array_filter($methods, function($v){        
        if($v->name == 'validate') { return false; }
        return strpos($v->name, 'validate') === 0;
    });
    
    //get the rule name, also if it has parameters
    $methods = array_map(function($v){
        $value = preg_replace('%^validate%','',$v->name); 
        $value = Str::snake($value);
    
        $params = $v->getParameters();
        $last   = array_pop($params);
        if($last && $last->name == 'parameters')
        {
            $value .= ':[params]';    
        }
        return Str::snake($value);
    }, $methods);
    
    var_dump($methods);
    

    If a user has added validation rules by extending the validation class, this technique will pickup any custom methods. However, if a user has extended the validation class with the Validation::extend syntax, the technique above will not find those rule. To get those rules, you'll need to do something like this.

    Validator::extend('customrule',function($attribute, $value, $parameters){
    });
    
    Validator::extend('anothercustom', 'FooValidator@validate');
    
    $validator = Validator::make(array(), array());
    
    $extension_methods = array();
    foreach($validator->getExtensions() as $value=>$callback)
    {
        if(is_string($callback))
        {
            list($class, $method) = explode('@', $callback);
            $r = new ReflectionClass($class);
            $method = $r->getMethod($method);
        }
        else if(is_object($callback) && get_class($callback) == 'Closure')
        {
            $method = new ReflectionFunction($callback);
        }
    
        $params = $method->getParameters();
        $last   = array_pop($params);
        if($last && $last->name == 'parameters')
        {
            $value .= ':[params]';    
        }
        $extension_methods[] = $value;
    
    }