Laravel validation rule "different" - php

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)

Related

Empty string validates for not required integer

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

Lumen provide a code for validation errors

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;
}

Laravel 5.1: custom validation with custom error message

I have custom validation which I added to my form request as:
public function rules()
{
return [
...
"firstCheckbox" => "required_with_one_of:secondCheckbox,thirdCheckbox",
...
];
}
Rule is that if the user selects firstCheckbox then he needs to select at least one more checkbox from given parameters: secondCheckbox or thirdCheckbox.
To make this happen I added to AppServiceProvider.php's boot function following code:
Validator::extend('required_with_one_of', function($attribute, $value, $parameters, $validator)
{
$data = $validator->getData();
foreach ($parameters as $p) {
if ( array_get($data,$p) != null) {return true;}
}
return false;
});
Validator::replacer('required_with_one_of', function($message, $attribute, $rule, $parameters) {
return str_replace(':values', implode(' / ', $parameters), $message);
});
All of this is working fine but if I'm not thinking of something please point it out so I can fix it.
My real problem is with the message. The message I receive is:
"You must select at least one of the fields: secondCheckbox, thirdCheckbox"
But what I want is to get message:
"You must select at least one of the fields: Second Checkbox, Third Checkbox"
To my lang\en\validation.php attributes[] I have added
'secondCheckbox' => 'Second Checkbox'
'thirdCheckbox' => 'Third Checkbox'
values, but this doesn't work because I never replace $parameters values in the Validator::replacer().
I have looked at Validator class and there is simple function that handle all of this
$parameters = $this->getAttributeList($parameters);
but I can't call that from Validator::replacer().
Is there a simple way to call this function from Validator::replacer() or should I just create CustomValidator class extending the Validator.
If the second way is the way to go can someone give me instructions how to do this. I've never really used PHP at this level so, although I have rough idea what to do, I'm not really sure how to do this.

Laravel custom validation - get parameters

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.

How to use multi required_if in laravel?

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

Categories