Kousha
Kousha

Reputation: 36189

Laravel Multiple Custom Validation

I'd like to add multiple custom validation, each as their own file.

So far, I've modified my app/start/global.php file to

global.php

ClassLoader::addDirectories(array(
    app_path().'/commands',
    app_path().'/controllers',
    app_path().'/models',
    app_path().'/database/seeds',
    app_path().'/validators'        // <--- Added this folder
));

// Only the first resolver works. 
// I cannot seem to have multiple of these files

Validator::resolver(function($translator, $data, $rules, $messages) 
{
    return new ScheduleValidator($translator, $data, $rules, $messages);
});

Validator::resolver(function($translator, $data, $rules, $messages) 
{
    return new UserValidator($translator, $data, $rules, $messages);
});

And each of my validation files would be in the /validators as

ScheduleValidator.php

class ScheduleValidator extends Illuminate\Validation\Validator
{
    protected $implicitRules = array('Required', 'RequiredWith', 'RequiredWithout', 'RequiredIf', 'Accepted', 'RequiredWithoutField');
    public function __construct(\Symfony\Component\Translation\TranslatorInterface $translator, $data, $rules, $messages = array())
    {
        parent::__construct($translator, $data, $rules, $messages);
        $this->isImplicit('fail');
    }

    /**
    * Validates type to be of the type 'common', 'template', or 'revision'
    */
    public function validateTypeSchedule($attribute, $value, $parameters = null)
    {
        $valid_types = ['template', 'common', 'revision'];
        return in_array($value, $valid_types);
    }

    // and the other validators ...
}

So how can I add multiple of these validators?

Upvotes: 3

Views: 3526

Answers (3)

Raul Duran
Raul Duran

Reputation: 548

Create one class with Custom Validator for example:

<?php namespace Acme\Validators;

use Illuminate\Validation\Validator as Validator;
use DB;
use Input;
use Request;

class CustomValidator extends Validator {

    public function validateUniqueWith($attribute, $value, $parameters)
    {
        $table = $parameters[0];

        $query = DB::table($table)
            ->where($parameters[1], Input::get($parameters[1]))
            ->where($attribute, $value);

        if (isset($parameters[3]))
        {
            list($idColumn, $id) = $this->getUniqueIds($parameters);
            $query->where('id', '!=', $idColumn);
        }

        if($query->count() > 0)
        {
            return false;
        }

        return true;
    }

    public function validateDateSame($attribute, $value, $parameters)
    {
        $this->requireParameterCount(1, $parameters, 'date_same');

        if ( ! ($date = strtotime($parameters[0])))
        {
            return strtotime($value) >= strtotime($this->getValue($parameters[0]));
        }
        else
        {
            return strtotime($value) >= $date;
        }
    }

    public function validateDni($attribute, $value, $parameters)
    {
        if(strlen($value)<9) {
            return false;
        }

        $value = strtoupper($value);

        $letra = substr($value, -1, 1);
        $numero = substr($value, 0, 8);

        // Si es un NIE hay que cambiar la primera letra por 0, 1 ó 2 dependiendo de si es X, Y o Z.
        $numero = str_replace(array('X', 'Y', 'Z'), array(0, 1, 2), $numero);

        $modulo = $numero % 23;
        $letras_validas = "TRWAGMYFPDXBNJZSQVHLCKE";
        $letra_correcta = substr($letras_validas, $modulo, 1);

        if($letra_correcta!=$letra) {
            return false;
        }
        else {
            return true;
        }
    }

}

And before resolver Validator in app/routes.php for example or in other file.

use Acme\Validators\CustomValidator as CustomValidator;

Validator::resolver(function($translator, $data, $rules, $messages)
{
    return new CustomValidator($translator, $data, $rules, $messages);
});

Upvotes: -1

Ezra
Ezra

Reputation: 1418

I solved this by making all my separate validators traits, so my one custom validators can simply 'use' my traits and still keep them separated.

My validator:

<?php

use Illuminate\Validation\Validator as LaravelValidator;

class CustomValidator extends LaravelValidator {
    use PhoneValidatorTrait;

}

My trait:

<?php

class PhoneValidatorTrait {
    public function validateSomething(){ ... }
}

resolver:

<?php

Validator::resolver(function($translator, $data, $rules, $messages)
{
    return new CustomValidator($translator, $data, $rules, $messages, []);
});

Upvotes: 6

Kryten
Kryten

Reputation: 15760

My guess is that the call to Validator::resolver simply sets a value if it's not already set, so the second and later calls are ignored.

What you really need is one call to Validator::resolver and include your logic for choosing which validator to use in the closure. It would look something like this:

Validator::resolver(function($translator, $data, $rules, $messages) 
{
    // perform a test to figure out what kind of validator to return
    if ($schedule) {
        return new ScheduleValidator($translator, $data, $rules, $messages);
    } else {
        return new UserValidator($translator, $data, $rules, $messages);
    }
});

The trick is goinge to be the if test - I'm not sure what to do there. The first thing that comes to mind is to check the type of $data:

if ($data instanceof Schedule) {

But the validator is going to receive an array instead of an object for $data. So that means either a) you need to examine the array values and puzzle out what it is you're trying to validate, or b) you need to add a flag or type value to the array when you're validating it. The second is likely to be easier and a little more robust. For example:

// in the Schedule controller
$data = Input::all();
$data["type"] = "schedule";
$validator = Validator::make($data, $rules);

// in global.php
Validator::resolver(function($translator, $data, $rules, $messages) 
{
    // perform a test to figure out what kind of validator to return
    if ($data["type"]=="schedule") {
        return new ScheduleValidator($translator, $data, $rules, $messages);
    } else {
        return new UserValidator($translator, $data, $rules, $messages);
    }
});

This is, unfortunately, not a very elegant solution. To improve it, you might create a library whose sole task is resolving the type of validator required.

Upvotes: 0

Related Questions