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
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 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)
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
I have been using the following validation for my form in Laravel:
public function isValid($data, $rules)
{
$validation = Validator::make($data, $rules);
if($validation->passes()){
return true;
}
$this->messages = $validation->messages();
return false;
}
The rules passed to it are simple:
$rules = [
'name' => 'required',
'type' => 'required'
];
And $data is the input post data. Now I need to add a custom validation extension to this, specifically to make sure that the value of input field round2 is greater than the value of input field round1. Looking at the docs, I have tried the following syntax which I think should be correct, but I keep getting an error.
$validation->extend('manual_capture', function($attribute, $value, $parameters)
{
return $value > $parameters[0];
});
Then I could call this with $attribute = 'round1', $value = $data['round1'] and $parameters = [$data['round2']].
The error is Method [extend] does not exist. - I'm not sure if my understanding of this whole concept is correct, so can someone tell me how to make it work? The docs only have about 2 paragraphs about this.
Put the following in your route.php
Validator::extend('manual_capture', function($attribute, $value, $parameters)
{
return $value > $parameters[0];
});
Additional documentation here
Then use it like so:
$rules = [ 'foo' => 'manual_capture:30'];
I want add an own rule that will check if my "active" flag is allowed to set.
I only want set this flag when this query get no result:
$playlist = Playlist::whereRaspberryId($raspberry_id)->whereActive(1)->first();
my validator rule is so far:
Validator::extend('onlyOneActive', function($attribute, $value, $parameters)
{
$playlist = Playlist::whereRaspberryId($parameters['raspberry_id'])->whereActive(1)->first();
if($playlist) {
return false;
}else{
return true;
}
});
My problem is the parameter. This is a value from another field in my form.
But how I get it?
This is my $rules array:
# Validate Rules
private static $rules = array(
'name' => 'required',
'active' => 'onlyOneActive:raspberry_id',
);
The problem:
In my own rule the parameter is only this:
Array
(
[0] => raspberry_id
)
This is logical. But I want the raspberry_id from my form, not the string..
How get I the id?
This doesn't work:
# Validate Rules
private static $rules = array(
'name' => 'required',
'active' => 'onlyOneActive:'.Input::get('raspberry_id'),
);
got error:
syntax error, unexpected '.', expecting ')'
Anyone an idea?
Thanks!
Finally I got it..
I use Input::get('raspberry_id) directly in my rule.
Is this a bad solution?
Validator::extend('onlyOneActive', function($attribute, $value, $parameters)
{
$playlist = Playlist::whereRaspberryId(Input::get('raspberry_id'))->whereActive(1)->first();
if($playlist) {
return false;
}else{
return true;
}
});
I got same problem, the reason is
You cannot use functions or variables when setting a variable on a class.
I think solution is not really good cause input name "raspberry_id"
can be different in other place where you would use this rule.
A bit better would be this:
https://stackoverflow.com/a/21208740/4581725.
Or you can not define $rules in class and define it in place in code where you use validation function.