I have to validate an input field of my API where the value has to be an integer between 1 and 100 or null or it is not even set (not required).
Thereby, my validation rule is: 'privacy_status' => "nullable|integer|min:1|max:100",
This works fine until I get an empty string as value. Since Laravel validation on a empty string validates only if field is implicit, all my other rules integer, nullable or min, max are ignored.
protected function isValidatable($rule, $attribute, $value)
{
return $this->presentOrRuleIsImplicit($rule, $attribute, $value) &&
$this->passesOptionalCheck($attribute) &&
$this->isNotNullIfMarkedAsNullable($attribute, $value) &&
$this->hasNotFailedPreviousRuleIfPresenceRule($rule, $attribute);
}
protected function presentOrRuleIsImplicit($rule, $attribute, $value)
{
if (is_string($value) && trim($value) === '') {
return $this->isImplicit($rule);
}
return $this->validatePresent($attribute, $value) || $this->isImplicit($rule);
}
Is there a way to validate this properly?
EDIT
You can always create a custom validation rule.
Validator::extendImplicit('fail_on_empty_string', function($attribute, $value, $parameters)
{
return $value === "" ? false : $value;
});
You can use the new rule like this:
'privacy_status' => "fail_on_empty_string|nullable|integer|min:1|max:100",
Old answer
This is described in the Laravel documentation and is a bug you introduced yourself (though probably unconsciously): https://laravel.com/docs/5.6/validation#a-note-on-optional-fields:
By default, Laravel includes the TrimStrings and ConvertEmptyStringsToNull middleware in your application's global middleware stack. These middleware are listed in the stack by the App\Http\Kernel class. Because of this, you will often need to mark your "optional" request fields as nullable if you do not want the validator to consider null values as invalid.
Empty string are automatically cast to null, which by your validation is perfectly fine. Either disable the middleware, change it or alter your validation rules.
You can simply pass it the filled rule. This will allow the field to be nullable, and if it's present in the request; it cannot be empty.
'privacy_policy' => 'filled|integer|min:1|max:100'
If you want to allow the empty string when it's present, change to present rule instead.
'privacy_policy' => 'present|nullable|integer|min:1|max:100'
Update
I've added an unit test to prove that this is working properly.
public function index()
{
request()->validate([
'privacy_policy' => 'filled|integer|min:1|max:100'
]);
return response();
}
Then in the test:
$this->get(route('test'))->assertStatus(200); // True
$this->get(route('test'), ['privacy_policy' => ''])->assertStatus(200); // False
$this->get(route('test'), ['privacy_policy' => 5])->assertStatus(200); // True
Related
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 am having a hard time understanding this validation rule. Basically, I have two fields, and they are both nullable. But, once both fields are filled, they have to be different from each other. I cannot enter test in both of them, for example. This validation rule works, if I fill both fields.
But, when I only fill one of the fields, the validation fails and says the fields should be different from each other with the following message:
The name and replace must be different.
I checked what is being submitted to my Form Request, and this is the following:
"name" => null
"replace" => "test"
Stripped version of my validation rules:
public function rules()
{
return [
'name' => 'different:replace|nullable',
'replace' => 'different:name|nullable',
];
}
Can somebody explain to me what I am misunderstanding with this validation rule? Do null values not count with this rule?
If you take a look at the validateDifferent function from Illuminate\Validation\Concerns\ValidatesAttributes (vendor/laravel/framework/src/Illuminate/Validation/Concerns/ValidatesAttributes.php:432) rule:
public function validateDifferent($attribute, $value, $parameters)
{
$this->requireParameterCount(1, $parameters, 'different');
foreach ($parameters as $parameter) {
$other = Arr::get($this->data, $parameter);
if (is_null($other) || $value === $other) {
return false;
}
}
return true;
}
As you can see in the if case, the rule will fail if the other value is null.
if (is_null($other) || $value === $other)
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
}
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.
1) I am new to laravel and want to integrate validation rules. My requirement is to make third field mandatory on basis of two other fields. Field C is required if both a and b are true. I have used required_if to put validation on basis of other single field but how can i use required_if to check two fields?
2) To achieve above functionality i tried custom validation rule as well. But it's working only if i will pull required rule alongwith.
For example:
'number_users' => 'required|custom_rule' //working
'number_users' => 'custom_rule' //Not working
You can use conditional rules for that.
Here's a simple example:
$input = [
'a' => true,
'b' => true,
'c' => ''
];
$rules = [
'a' => 'required',
'b' => 'required'
// specify no rules for c, we'll do that below
];
$validator = Validator::make($input, $rules);
// now here's where the magic happens
$validator->sometimes('c', 'required', function($input){
return ($input->a == true && $input->b == true);
});
dd($validator->passes()); // false in this case
Laravel evaluates each rule in the giver order. Let's say:
'number_users' => 'required|custom_a|custom_b'
custom_b rule will be evaluate when required and custom_b are true because these rules were already evaluated.
You can also create a generic validator to test the AND operator.
Validator::extend('required_multiple', function ($attribute, $value, $parameters, $validator) {
$isRequired = true;
for ($i = 0; $i <= count($parameters) / 2; $i = $i + 2) {
$parameter = $parameters[$i];
$value = $parameters[$i + 1];
if (in_array($value, ['true', 'false'])) {
$value = (bool)$value;
}
if ($validator->getData()[$parameter] !== $value) {
$isRequired = false;
}
}
if (!$isRequired || ($isRequired && $validator->getData()[$attribute])) {
return true;
} else {
return false;
}
});
Then in your controller rules:
'phone_input_label' => 'required_multiple:goal,lead-capture,phone_input_active,true|string',
This will make "phone_input_label" required if goal is equal to "lead-capture" and phone_input_active is true.
The only problem with that custom validator is that you need to send all parameters. If "phone_input_label" is not sent in the request, it won't even pass through the custom validator, not sure why it happens.
According to documentation you can make a complex required_if statement by using requiredIf mothod of validation Rule.
If you would like to construct a more complex condition for the required_if rule, you may use the Rule::requiredIf method. This method accepts a boolean or a closure. When passed a closure, the closure should return true or false to indicate if the field under validation is required:
use Illuminate\Validation\Rule;
Validator::make($request->all(), [
'number_users' => Rule::requiredIf(function () {
return $this->input('input_a') == 1 && $this->input('input_b') == 2;
}),
]);
Here is your answer
'field3' => 'required_if:field_1 | required_if:field_2'
Take a look at the laravel validation docs which I pasted a link below, you can use required_if, required_without and others to suit your needs.
See the Laravel Validation Docs Here