How to use a closure as a validator? - validation

Is it possible to do something similar to the following in Laravel:
public function rules()
{
return [
'sid' => function ($input) {
// some custom validation logic.
}
];
}
public function messages()
{
return [
'sid' => "Invalid SID!",
];
}
I want to do some simple single-use validation. Creating a custom validation is an overkill.

If you are using Laravel 5.6 or later, you may use closures.
To have $input available in the scope of the closure, you may use use keyword.
'sid' => [ function($attribute, $value, $fail) use $input {
// your logic here
//if that fails, so
return $fail($attribute.' is invalid.');
}
]

There are two options here, at least.
Create a custom rule via AppServiceProvider at boot() method:
Validator::extend('my_rule', function($attribute, $value, $parameters) {
// some custom validation logic in order to return true or false
return $value == 'my-valid-value';
});
then, you apply the rule like:
return [
'sid' => ['my_rule'],
];
Or extending ValidatorServiceProvider class. Use this thread explained step by step: Custom validator in Laravel 5

Related

Laravel custom validation rule refering to other request params

I'm trying to create a custom validation rule that accept a parameter, but this parameter is the name of another field in the request, like for the required_with rule.
I easily can handle given params in my rule, but i'm struggling to find out how to retrieve the other field value.
Currently i'm creating my rule class as
class MyClassRule
{
public function validate($attribute, $value, $parameters, $validator) : bool
{
// do some stuff here to return true/false
}
}
and registering it in my service provider with
Validator::extend('my_rule', 'path\to\MyClassRule#validate');
so i can use it in my request as
public function rules()
{
return [
'field' => ['my_rule'],
];
}
What i would like to be able to do is
public function rules()
{
return [
'other_field' => [...],
'field' => ['my_rule:other_rule'],
];
}
and use the other_field value in my rule class, but validate()'s $parameters value is just ['other_field']. i.e. an array containing the other field name, not its value.
How can i do this?
I'm running this in Laravel 7.x.
In my case, I am trying to make a rule to compare whether two field in my form is equal to one another.
Let's make a new Rule Object as instructed from Laravel's documentation.
https://laravel.com/docs/7.x/validation#custom-validation-rules
Below is the console command to make the Rule class template.
php artisan make:rule StrEqualTo
Below is the generated custom Rule class with the full implementation of the logic.
<?php
namespace App\Rules;
use Illuminate\Contracts\Validation\Rule;
class StrEqualTo implements Rule{
private $referredField = null;
public function __construct(string $referredField){
$this->referredField = $referredField;
}
public function passes($attribute, $value){
return request()->input($this->referredField) === $value;
}
public function message(){
return 'The :attribute must match with' . $this->referredField . '.';
}
}
We first create a private attribute and a constructor with a parameter, the parameter will accept the 'name' attribute of the field you want to refer. We then assign the value from the constructor parameter to our private attribute in our rule class.
private $referredField = null;
public function __construct(string $referredField){
$this->referredField = $referredField;
}
As stated in Laravel's docs, this function must return true if the validation succeeds, otherwise it must return false. What we do here is to use the request() helper function provided by Laravel and get the value of the field we referred from the form input($this->referredField).
public function passes($attribute, $value){
return request()->input($this->referredField) === $value;
}
We can edit the error message it will create when the validation failed in this function below.
public function message(){
return 'The :attribute must match with' . $this->referredField . '.';
}
We then instantiate the custom Rule class to an object to be used as validation rule like the code below.
'confirm-new-pass' => ['required', 'string', 'max:100', new StrEqualTo('new-pass')]
Hope this helps!!!
Artisan command
php artisan make:rule ValidateOtherField
Class ValidateOtherField
class ValidateOtherField implements Rule
{
private $error = '';
public function passes($attribute, $value)
{
if(request()->has('field') && request()->get('field') === 'MyValueSuccess'){
if(is_string($value)){
return true;
} else {
$this->error = '- not valid field';
}
}
return false;
}
public function message()
{
return "Error :attribute {$this->error}";
}
}
rules
public function rules()
{
return [
'field' => ['string'], //Validate field
'other_field' => [new ValidateOtherField],
];
}
Because $validator is a full instance of the Validator object being used, we can retrieve data from it using getData():
public function validate($attribute, $value, $parameters, $validator)
{
// You may want to check to make sure this exists first.
$otherField = $parameters[0];
$otherValue = data_get($validator->getData(), $otherField);
// #todo Validate $otherValue
}
Using data_get() allows you to use dot notation for nested array values as well.

Laravel 5.3 Form Request Validation required_if and min rules

I use Form Request Validation in one of my Laravel projects, and want to validate country_id but only if bak_leaflet ist set to 0. In my FormRequestFile i have the following rules:
public function rules()
{
return [
...
'country_id' => 'required_if:bak_leaflet,0',
...
];
}
This works absolutely fine, but when bak_leaflet is 0, then country_id also needs to be larger than 1:
public function rules()
{
return [
...
'country_id' => 'required_if:bak_leaflet,0|min:1',
...
];
}
However, the min:1 rule gets ignored completely. How can I make sure the validation works how I need it to?
You can add a validation extender in your AppServiceProvider's boot() method, like this:
\Validator::extend('min_if', function ($attribute, $value, $parameters, $validator) {
$data = $validator->getData();
if (isset($data[$parameters[0]]) && $data[$parameters[0]] == $parameters[1] && (int)$value < $parameters[2]) {
return false;
}
return true;
});
Then write your validation rule like this:
'country_id' => 'required_if:bak_leaflet,0|min_if:bak_leaflet,0,1',
Additionally you will need to add
'min_if' => 'Your validation message',
into resources/lang/en/validation.php or pass to your $this->validate() as third paramater
I've used the answer of #avik-aghajanyan and added the method to my form request file as follows:
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Factory as ValidationFactory;
class StoreArticle extends FormRequest
{
public function __construct(ValidationFactory $validationFactory)
{
$validationFactory->extend('min_if', function ($attribute, $value, $parameters, $validator) {
$data = $validator->getData();
if (isset($data[$parameters[0]]) && $data[$parameters[0]] == $parameters[1] && (int)$value < $parameters[2]) {
return false;
}
return true;
});
}
....

laravel custom validation return empty errors when I defined it in the ServiceProvider

When I defined a custom validation in the Validated Service Provider. And when it is failed I got empty MessageBox. I don't understand why it is happens.
UPDATED:
<?php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
class ValidatorServiceProvider extends ServiceProvider
{
public function boot()
{
\Validator::extend('custom_validation', function ( $attribute, $value, $parameters ) {
return false;
} );
}
}
in the controller
private function isValid()
{
$this->validation = Validator::make( $this->attributes, [
'input' => 'custom_validation'
] );
dd($this->validation->errors()); //returned
// MessageBag{#668 #messages: [] #format: ":message" }
}
}
UPDATE2:
my app.php file
'providers' => [
...
App\Providers\ValidatorServiceProvider::class,
],
To create a Custom Validator:
Step 1:
-> Create a Service provide php aritsan make:proider ValidatorServiceProvider
Step 2: add the following to your service provider in boot method
public function boot()
{
Validator::extend('custom_validation', function($attribute, $value, $parameters, $validator) {
//checks if input value is 1
if($value = 1) return true;
return false;
});
}
Step 3:
Register your service provide to config/app.php in provider section
App\Providers\ValidatorServiceProvider::class
Step 4 (Laravel way)
Create a request to validate all of your input field
php artisan make:request CustomValidationRequest
Step 5:
Add the following to the request
//validation rules
public function rules()
{
return [
'digit' => 'required|custom_validation'
];
}
//message
public function messages()
{
return [
'digit.required' => 'This value is required.',
'digit.custom_validation' => 'This custom validation is required'
];
}
Now use it in you controller
public function store(Request CustomValidationRequest)
{
//validation will be done and send back to view if error occurred
}
Hopefully this will help
Cheers
Are you using the laravel Validator facade ?
if so you do not need to define it from the service provider?

Laravel 5.2 | Set Custom Attribute with Calculcated Value On The Fly Never Replaced

I've a custom validator, see below (simplified)
Form Request
public function rules()
{
return [
'amount' => 'required|numeric|max_invest:10000'
];
}
public function messages()
{
return [
'max_invest' => 'You can invest max :mi' // I want to set :mi on the fly
];
}
Validator
public function validateMaxInvestment($attribute, $value, $parameters, $validator)
{
$this->setAttributeNames(['mi' => intval($parameters[0] - $value)]); // Try to set the attribute mi on the fly
return $value < $parameters[0];
}
I did register the validator in the boot method of my service provider, like so:
$this->app['validator']->extend('maxInvestment',
'MaxInvestmentValidator#validateMaxInvestment');
The problem
The validator works fine, but the message I get stays:
You can invest max :mi
Calling the method setAttributeNames doesn't take effect.
You need to use a replacer for that purpose which you may miss as it is at the bottom of the documentation.
When creating a custom validation rule, you may sometimes need to
define custom place-holder replacements for error messages. You may do
so by creating a custom Validator as described above then making a
call to the replacer method on the Validator facade. You may do this
within the boot method of a service provider:
public function boot() {
Validator::extend(...);
Validator::replacer('foo', function($message, $attribute, $rule, $parameters) {
return str_replace(...);
});
}
So for your case, something like below should work.
protected function validateMaxInvestment($attribute, $value, $parameters, $validator)
{
$replace = intval($parameters[0] - $value);
$validator->addReplacer('max_invest', function ($message) use ($replace) {
return str_replace(':mi', $replace, $message);
});
return $value < $parameters[0];
}
Also, not sure but I guess you can also do something like below.
protected function validateMaxInvestment($attribute, $value, $parameters, $validator)
{
return $value < $parameters[0];
}
protected function replaceMaxInvestment($message, $attribute, $rule, $parameters)
{
$replace = intval($parameters[0] - \Input::get($attribute));
return str_replace(':mi', $replace, $message);
}
Hence, probably you will need to register it again.
$this->app['validator']->replacer('maxInvestment', 'MaxInvestmentValidator#replaceMaxInvestment');

Laravel: Add custom validation class make fail attributes() method in Request class

I have code in Request class:
public function rules()
{
return [
'id' => 'required|check_xxx',
];
}
public function attributes()
{
return [
'id' => 'AAA',
];
}
As you can see. I have cusom validation method name check_xxx. This method in inside class CustomValidator.
So, I have code:
class ValidationServiceProvider extends ServiceProvider
{
public function boot()
{
$this->app->validator->resolver(function ($translator, $data, $rules, $messages) {
return new CustomValidator($translator, $data, $rules, $messages);
});
}
}
And error message for required is: Please input :attribute
But I got the message: Please input id, (TRUE is: Please input AAA)
I discovered that $this->app->validator->resolver make attributes() method in Request is useless.
How can I fix that? Thank you.
I had this issue in Laravel 5.2 but found a QUICK solution as following. In example following you will add rule directly inside the APP Provider.
File to add rule: app/Providers/AppServiceProvider.php
public function boot()
{
// ....
#/
#/ Adding rule "even_number" to check even numbers.
#/
\Validator::extend('even_number', function($attribute, $value, $parameters, $validator) {
$value = intval($value);
return ($value % 2) == 0);
// ...
}

Resources