Custom Validation Function in Laravel 5

Published 3 years ago by nschiffelbein

I would like to write a function in my FormRequest object to perform some custom validation steps in Laravel 5, but I can't seem to find a function to override/implement to perform these steps. What is the best approach to do something like that?

danielboendergaard

Override the validate method, you probably missed it because it's defined in a trait:

public function validate()
{
    parent::validate();

    // Do something
}
nschiffelbein

danielboendergaard, Thanks for your response and pointing me toward the trait. Overriding the validate function seems like it forces you to re-implement the core handling of valid/invalid data. Looking through the trait I saw the getValidatorInstance() method and came up with this solution based on a post here (http://mattstauffer.co/blog/laravel-5.0-form-requests):

public function getValidatorInstance() {
        $validator = parent::getValidatorInstance();

        $validator->after(function() use ($validator) {
            // Do check here

            if(// Invalid condition)
                $validator->errors ()->add('field', 'message');
        });

        return $validator;
}

Does this seem like a clean way to add the additional conditions or will overriding this method cause problems down the road?

keevitaja
alexleonard

Thanks @nschiffelbein, you just set me on the right path!

Not sure if this is exactly how I should be doing it but it does work

public function getValidatorInstance() {
    $validator = parent::getValidatorInstance();


    $validator->after(function() use ($validator) {
        $input = $this->formatInput();

        $start = $this->carbon->parse($input['start']);
        $end = $this->carbon->parse($input['end']);

        if($start > $end) {
            $validator->errors()->add('start', 'Start date must be later than end date');
        }

        if($start->diffInMinutes($end) > 180){
            $validator->errors()->add('end', 'The end time must be within three hours of the start');
        }
    });

    return $validator;
}
dbwhddn10

or now you can do like this when you want to move validator method to custom class in request class

class TestRequest extends Request {

    public function validator($factory)
    {
        $factory->resolver = function ($translator, $data, $rules, $messages)
        {
            new TestValidator($translator, $data, $rules, $messages);
        }

        return $factory->make($this->formatInput(), $this->container->call([$this, 'rules']), $this->messages());
    }

}
bestmomo
bestmomo
2 years ago (373,920 XP)

To create a new rule just extend Validator, for example :

<?php namespace app\Services;

use Illuminate\Validation\Validator;

class Validation extends Validator {

    public function validateTags($attribute, $value, $parameters)
    {
        return preg_match("/^[A-Za-z0-9-éèàù]{1,50}?(,[A-Za-z0-9-éèàù]{1,50})*$/", $value);
    }

} 

And declare it in service provider :

use App\Services\Validation;

public function boot()
{
    Validator::resolver(function($translator, $data, $rules, $messages)
    {
        return new Validation($translator, $data, $rules, $messages);
    });
}
novica.vukobratovic

Take note that the class you should be using in the boot() method of Validator, should be an instance of the Validator Facade and not Illuminate\Validation\Validator.

pmall
pmall
2 years ago (581,145 XP)

@nschiffelbein as @bestmomo said, you should use custom validation rules if you want to perform custom validations.

stonebitsrl

Can anybody explain me what is wrong with my code?

I created CustomValidation class

<?php namespace App\Services\Validation;

use Illuminate\Validation\Validator;

class CustomValidation extends Validator{
     //added only for test
    public function validateTest($attribute, $value, $parameters)
    {
        return false;
    }
}

I created service provider class

<?php namespace App\Services\Validation;

use Illuminate\Support\ServiceProvider;

use App\Services\Validation\CustomValidation;

class ValidationServiceProvider extends ServiceProvider{

    public function register(){}

    public function boot()
    {
        $this->app->validator->resolver(function($translator, $data, $rules, $messages)
        {
            return new CustomValidation($translator, $data, $rules, $messages);
        });
    }
}

I registered my service provider in app.php.

I'm trying to use CustomValidation in Registrar.php

<?php namespace App\Services;

use App\User;
use Validator;
use Illuminate\Contracts\Auth\Registrar as RegistrarContract;

class Registrar implements RegistrarContract {

    public function validator(array $data)
    {
        return Validator::make($data, [
                'username' => 'test',
        ],
               [
            'username.test' => 'Test.',
        ]
        );
    }
}

But this not works for me, it never fires. I had tried add die in my validation class in function validateTest but it not fired. Can anybody tell me what is my problem.

Thanks.

stonebitsrl

OK. I found my bug. I had sent empty field on server and custom validation was not worked.

PascalBoucher

Hey guys, if you are actually creating custom required rules, make sure you overwrite the $implicitRules attribute and add in the array the name of your custom required rule.

Otherwise you will run into the same bug than @stonebitsrl , because Laravel won't fire the validation when field is empty unless the rule is specified into $implicitRules.

Example :

<?php

namespace App\Services;

use Illuminate\Validation\Validator;

class CustomValidator extends Validator {

    /**
     * The validation rules that imply the field is required.
     *
     * @var array
     */
    protected $implicitRules = [
        'Required', 'RequiredWith', 'RequiredWithAll', 'RequiredWithout',
        'RequiredWithoutAll', 'RequiredIf', 'Accepted', 'CustomRequiredA',
        'CustomRequiredB'
    ];

public function validateCustomRequiredA($attribute, $value) {
    return true;
}

public function validateCustomRequiredB($attribute, $value) {
    return true;
}

Or if you prefer you can also do it in the constructor, maybe a more reliable way. Don't forget to import Symfony\Component\Translation\TranslatorInterface at the top if you are going to use this method.

    public function __construct(TranslatorInterface $translator, array $data, array $rules, array $messages = array(), array $customAttributes = array()) {

        parent::__construct($translator, $data, $rules, $messages, $customAttributes);
        array_push($this->implicitRules, "CustomRequiredA", "CustomRequiredB");
    }

That confused me at first, so I hope it can help someone in the futur.

godigitalweb

Laravel 5.2 Custom Validation - Full Code Example

STEP 1 of 3: Configure App Service Provider

app/Providers/AppServiceProvider.php

This file should already exist. You'll notice all that has been added for this example is in the boot method and the use Validator;

<?php

namespace App\Providers;

use Illuminate\Support\ServiceProvider;
use Validator;

class AppServiceProvider extends ServiceProvider
{
    public function boot()
    {
        Validator::extend('strength', 'App\Http\CustomValidator@validateStrength');
    }

    public function register()
    {
        //
    }
}

STEP 2 of 3: Create Custom Validator Class

app/Http/CustomValidator.php

You can put this where ever you want and namespace it accordingly. The method name of the rule should be prepended with 'validate'.

NOTE! This class CANNOT extend the Validator class and if you do it throws and error: 'Unresolvable dependency resolving [Parameter #1...'

<?php

namespace App\Http;

class CustomValidator {

    public function validateStrength($attribute, $value, $parameters, $validator)
    {
        if( preg_match('/(?=^.{8,}$)(?=.*\d)(?=.*[[email protected]#$%^&*]+)(?![.\n])(?=.*[A-Z])(?=.*[a-z]).*$/', $value) )
            return true;

        return false;
    }

}

STEP 3 of 3: Edit Language File

resources/lang/en/validation.php

This file is basically a return statement with all of the default error messages. Here is an example of where your message will go as 'custom' is mentions a few places in the file and may be confusing to some. Of course you can place it anywhere as long as it's in the correct depth.

   /*
    |--------------------------------------------------------------------------
    | Custom Validation Language Lines
    |--------------------------------------------------------------------------
    |
    | Here you may specify custom validation messages for attributes using the
    | convention "attribute.rule" to name the lines. This makes it quick to
    | specify a specific custom language line for a given attribute rule.
    |
    */
    'strength' => 'The password :attribute is too weak and must contain one or more uppercase, lowercase, numeric, and special character ([email protected]#$%^&*).',

    'custom' => [
        'attribute-name' => [
            'rule-name' => 'custom-message',
        ],
    ],

Final Thoughts

At this point you can use this validation rule. For example in a request...

public function rules()
    {
        return [
            'first_name' => 'required|max:255',
            'last_name'  => 'required|max:255',
            'email'      => 'required|email|max:255|unique:users',
            'password'   => 'required|min:8|strength|confirmed',
        ];
    }
SteveBelanger

@godigitalweb I must be close to the solution, but not there yet, as I get this message :

BadMethodCallException in Validator.php line 2678: Method [validateScheduleConflicts] does not exist.

I use laravel 5.1 , I asked about this here, but to no avail.
I did the modifications your post suggest, and I still get the error.

ipunkt
ipunkt
1 year ago (17,295 XP)

The given example by @godigitalweb fails with BadMethodCallException in Validator.php line 3394: Method [validateStrength] das not exists on Laravel 5.3

Ober4You

@godigitalweb it would be nice if you could help us with this... i also get an error BadMethodCallException. Can you test it and verify it with Laravel 5.3?

Please sign in or create an account to participate in this conversation.