The Issue
Consider this:
protected $rules = array(
'mobile_number' => 'phone:AUTO,mobile,:country_code'
);
In the above example, the value of country_code needs to change dependant on a variable defined prior to the validation taking place.
With this in mind, is it possible to pass a variable into a Laravel Validation rule?
Please bear in mind, this is how I call my validation:
if(!$this->some_validator->with($data)->passes()){
// Get the validation errors and throw the exception
$error_info = $this->some_validator->formatErrorMessages();
throw new ExampleException(ExampleExceptionType::$VALIDATION_ERROR,$error_info);
}
You are allowed to create custom validation rules.
It looks like this:
Validator::extend('country_code', function($attribute, $value, $parameters, $validator) {
if ($value === 'US') {
return false;
}
return true;
});
And then just use it like this:
protected $rules = array(
'mobile_number' => 'phone:AUTO,mobile|country_code'
);
After re-reading your message I realized that you meant a bit different thing. But take a look at the $parameters argument.
And read documentation. It's quite well covered there https://laravel.com/docs/5.3/validation#custom-validation-rules
With the new way of doing this, if you extend a new rule and you have a variable that you want to pass to the closure, you can do it like this:
Validator::extend(nameOfTheRule, function ($attribute, $value, $parameters, $validator) use ($priorVariable) {
//code
}
Related
I am using the Lumen Framework, which utilizes the Laravel Validation
I wanted to create a Validator Rule to make the Request->input() json only contain specific keys at the root like "domain" and "nameservers". Not more and not less.
Example passing the rule:
{
"domain":"domain.tld",
"nameservers":
{...}
}
Example not passing the rule:
{
"domain":"domain.tld",
"nameservers":
{...},
"Hack":"executeSomething()"
}
I tried to use to use several default validation rules to achieve this but wasnt successful.
My approach was now to put the request in another array like this
$checkInput['input'] = $request->all();
to make the validator validate the "root" keys.
Now this is my Approach:
create the validator
$checkInput['input'] = $request->all();
$validator = Validator::make($checkInput, [
'input' => [
'onlyContains:domain,nameservers'
],
]);
creating the rule
Validator::extend('onlyContains', function($attribute, $value, $parameters, $validator){
$input = $validator->getData();
$ok = 0;
foreach ($parameters as $key => $value) {
if (Arr::has($input, $attribute . '.' . $value)) {
$ok++;
}
}
if (sizeof(Arr::get($input, $attribute)) - $ok > 0) {
return false;
}
return true;
});
It seems i got the desired result, but i am asking if there is maybe smarter solution to this with the default rules provided by Laravel/Lumen.
You are trying to do a blacklisting approach blocking out fields that are not intended. A simple approach, that is utilized a lot, is to only fetch out the validated. Also you are trying to do logic, that goes against normal validation logic, to do it a field at a time.
This is also a good time, to learn about FormRequest and how you can get that logic, into a place where it makes more sense.
public function route(MyRequest $request) {
$input = $request->validated();
}
With this approach, you will only ever have the validated fields in the $input variable. As an extra bonus, this approach will make your code way easier to pick up by other Laravel developers. Example form request below.
public class MyRequest extends FormRequest
{
public function rules()
{
return [
'domain' => ['required', 'string'],
'nameservers' => ['required', 'array'],
];
}
}
You should use prohibited rule.
For eg:
$allowedKeys = ['domain', 'nameservers'];
$inputData = $request->all();
$inputKeys = array_keys($inputData);
$diffKeys = array_diff($inputKeys, $allowedKeys);
$rules = [];
foreach($diffKeys as $value) {
$rules[$value] = ['prohibited'];
}
I'm using Laravel rules and I want to make a validation which requires multiple attributes.
For example, I want a rule to check that the quantity requested doesn't exceed the available stock for the given product. So, something like
public function rule() {
return [
'quantity produyctId' => "checkQty"
}
I would prefer to solve it using rules but other methods are also acceptable.
You can create a custom validation from extending the validation.
In AppServiceProvider class
Validator::extend('quantity_validity', function ($attribute, $value, $parameters, $validator) {
$productId = $parameters[0];
$quantity = $value;
// you can do whatever with these,
// and finally return true or false according to your desire.
});
In Validation
public function rule() {
return [
'quantity' => "quantity_validity:{$productId}"
]
}
I'm trying to add custom validation logic for file uploads for my admin panel. Right now my file fields can return either Illuminate\Http\UploadedFile or string|null if the file is not uploaded or changed or whatever. What I'm doing is, I created a custom rule that looks like this:
'image' => [
'required',
'admin_file:mimes:jpeg;png,dimensions:min_width=800;min_height=600'
]
I then parse all the arguments I pass, and the thing is, I naturally want all of them applied only if my value is an instance of UploadedFile. I use the following code for my custom validation:
<?php
class AdminFileValidator
{
public function validate($attribute, $value, $parameters, Validator $validator)
{
$rules = implode(
"|",
array_map(function($item) {
return str_replace(";", ",", $item);
}, $parameters)
);
$validator->sometimes($attribute, $rules, function() use ($value) {
return $value instanceof UploadedFile;
});
return true;
}
}
The problem is with adding additional rules to an attribute via sometimes doesn't work that way. The added rules are not being processed by a validator.
Is there any way to validate these rules without revalidating the whole thing manually?
What I see is that your are using sometimes inside of a rule. From my perspective you need to take it out, even better without use a custom class.
Using Validator object:
$validator = Validator::make($data, [
'image' => 'required',
]);
$validator->sometimes('image', 'mimes:jpeg;png,dimensions:min_width=800', function($value) {
return $value instanceof UploadedFile;
});
If you are using a Request class you could override the function getValidatorInstance in order apply the conditional rules:
protected function getValidatorInstance(){
$validator = parent::getValidatorInstance();
$validator->sometimes('image', 'mimes:jpeg;png,dimensions:min_width=800', function($value) {
return $value instanceof UploadedFile;
});
return $validator;
}
Currently in lumen when you use the $this->validate($request, $rules) function inside of a controller it will throw a ValidationException with error for your validation rules(if any fail of course).
However, I need to have a code for every validation rule. We can set custom messages for rules, but I need to add a unique code.
I know there's a "formatErrorsUsing" function, where you can pass a formatter. But the data returned by the passed argument to it, has already dropped the names of the rules that failed, and replaced them with their messages. I of course don't want to string check the message to determine the code that should go there.
I considered setting the message of all rules to be "CODE|This is the message" and parsing out the code, but this feels like a very hacked solution. There has to be a cleaner way right?
I've solved this for now with the following solution:
private function ruleToCode($rule) {
$map = [
'Required' => 1001,
];
if(isset($map[$rule])) {
return $map[$rule];
}
return $rule;
}
public function formatValidationErrors(Validator $validator) {
$errors = [];
foreach($validator->failed() as $field => $failed) {
foreach($failed as $rule => $params) {
$errors[] = [
'code' => $this->ruleToCode($rule),
'field' => $field,
];
}
}
return $errors;
}
I want to get the parameter passed in the validation rule.
For certain validation rules that I have created, I'm able to get the parameter from the validation rule, but for few rules it's not getting the parameters.
In model I'm using the following code:
public static $rules_sponsor_event_check = array(
'sponsor_id' => 'required',
'event_id' => 'required|event_sponsor:sponsor_id'
);
In ValidatorServiceProvider I'm using the following code:
Validator::extend('event_sponsor', function ($attribute, $value, $parameters) {
$sponsor_id = Input::get($parameters[0]);
$event_sponsor = EventSponsor::whereIdAndEventId($sponsor_id, $value)->count();
if ($event_sponsor == 0) {
return false;
} else {
return true;
}
});
But here I'm not able to get the sponsor id using the following:
$sponsor_id = Input::get($parameters[0]);
As a 4th the whole validator is passed to the closure you define with extends. You can use that to get the all data which is validated:
Validator::extend('event_sponsor', function ($attribute, $value, $parameters, $validator) {
$sponsor_id = array_get($validator->getData(), $parameters[0], null);
// ...
});
By the way I'm using array_get here to avoid any errors if the passed input name doesn't exist.
http://laravel.com/docs/5.0/validation#custom-validation-rules
The custom validator Closure receives three arguments: the name of the
$attribute being validated, the $value of the attribute, and an array
of $parameters passed to the rule.
Why Input::get( $parameters ); then? you should check $parameters contents.
Edit.
Ok I figured out what are you trying to do. You are not going to read anything from input if the value you are trying to get is not being submitted. Take a look to
dd(Input::all());
You then will find that
sponsor_id=Input::get($parameters[0]);
is working in places where sponsor_id was submited.