How to create a custom exclude validation rule in Laravel - laravel

I would like to create a custom rule that excludes an attribute under some condition, the only ways I could think about are:
manually verify the condition in the FormRequest and add the 'exclude' rule if my condition is ture (which is, imo, not so elegant)
(I'm not sure if this will work/ won't have side effects) create a custom validation rule that will remove the attribute from the request (request->remove($attribute)) if the condition is true.
Is there any better way to do this ?
I couldn't find the implementation of any of the exclude rules either.

This is a vague answer to match your vague question, but it is easily done with conditional complex validation. Use the withValidator() method to access the validator instance before the rules are applied.
// assuming we're inside a FormRequest
public function withValidator($validator)
{
$validator->sometimes(
'some_field',
'required|string|max64',
fn ($data) => $data->some_other_field !== 1234
);
}
First argument to sometimes() is the field you want to conditionally validate; the second is the rules you want to apply; and the third is a callback. The callback is passed the request data; if it returns true, the rules will be applied.

There are lot of ways to exclude a validation rule based on conditions.
You can use exclude_if, exclude_unless and exclude_without.
Suppose you want to exclude a field validation if its another field's value is null, then you can use
['field1' => 'exclude_if:field2,null|<validation rules for field1>']
Again suppose you want to exclude a field validation unless the other value is not equal to 3, then use,
['field1' => 'exclude_unless:field2,==,3|<validation rules for field1>']

I've just found out that since Laravel 8.55 there is the method 'when' in the Rule object that you can use inside your rules() method as follow to exclude an attribute:
Rule::when(MyCallBackThatReturnsABoolean, [/*RulesIfCallBackReturnsTrue*/], [/*RulesIfCallBackReturnsFalse*/])
For some reason, I can't find the method in the documentation.
for example to exclude an attribute if the condition foo==bar is false I could do:
Rule::when(function() use(foo,bar) { return foo===bar }, ['required', 'string'], ['exclude'])
Pull requests here and here
Example of usage here

Related

Optional query with a lot of parameter

I must have build a query from my store items. my store items have 10 field . I just let customer search in my item's optional.
for example first one maybe want to filter on field1 , second one maybe want to filter on field1 and field2,third one maybe want to filter on field6 and field8 and filde9,... .
how can i make a query more short more efficient for this ?
note 1: I don't want to use Raw method because of its vulnerability.
note 2: i see some answers in link 1 and link 2 but I think first one can not be use for
condition like : where('field1','>=','somevalue') or where('field2','like','%somevalue%') or any sort of condition with some complexity and second one has more "if" chaining and i want to have shorter than this if it's possible
You can do this in several ways depending on the syntax you'd like. One possible way is to use a separation symbol to pass multiple arguments:
/api/user?where[]=user_id|>=|5
/api/user?where[]=user_id|2
/api/user?where[]=user_id|2&where[]=status|activated
This simply allows you to pass multiple where options where the pipe | operator separates the arguments. Note that this may cause issues if you want the pipe character to be available as search argument for instance.
Then you could simply parse this url into your query like so:
foreach($request->get('where') as $i => $values) {
$values = explode('|', $values);
// Allow the third argument to be exchanged with the second one,
// similar to how the Laravel `where()` method works.
$query->where($values[0], $values[1], $values[2] ?? null);
}
Optionally, you can add a validation method so that the syntax will be properly checked beforehand. You can add this snippet to some boot() method of a service provider:
\Illuminate\Support\Facades\Validator::extend('query_where', function($attribute, $value) {
// Require minimum length of 2 parameters.
return count(explode('|', $value)) >= 2;
});
Next, your validation in your controller action would look like this:
$this->validate($request, [
'where' => 'nullable|array',
'where.*' => 'query_where',
]);
The possibilities are endless.

Laravel Request / Validation always return string type values

In laravel 7, the $request->all() or the $validator->valid(), always return an array of values, key assign is correct, but the values are always strings.
I need the validator to transform the field to the rules i made.
Example in rules for validation : ['no' => 'required|integer|min:1',...]
Example of the output of validation->valid() : [ "no" => "1231" ] - string typed, i need this to be integer like : [ "no" => 1231 ]
I dont want to cast every field one by one... what i'm doing wrong ?
NOTE
All the validations works well, it's only the output of the fields that i want to match the validation, if i say the field is integer, the result of the validation must be integer and not string.
I just resolve this problem :
Create a FormRequest file, and put the validations, rules and messages there.
I dont know why... in the last version : my validations , rules and messages stay in the controller file.
Clear cache, everything ok.

How to have a CakePHP model field requirement checked manually?

QUESTION UPDATED: I found out more information and therefor changed my question.
I want to save my user with his license number, but only if he selects that he has a license. I have three possible values for the pilot_type field (ROC, ROC-light and recreative) and only the last option should allow for an empty string to be submitted.
In order to achieve this, I wrote the following validation function:
$validator
->add('license_nr', 'present_if_license', [
'rule' => function ($value, $context) {
return $context['data']['pilot_type'] == 'recreative' || !empty($value);
},
'message' => "If you don't fly recreatively, a license number needs to be supplied"
]);
The problem is that setting any validation rule on a field will trigger an additional check in the CakePHP Model class that will reject the value if it's empty. I tried to fix this by adding ->allowEmpty('license_nr'), but that rule makes for the model to accept an empty string without even running my custom function. Even putting them in order and using 'last' => true on my custom rule doesn't resolve this problem:
$validator
->add('license_nr', 'present_if_license', [
'rule' => function ($value, $context) {
return false;
// return $context['data']['pilot_type'] == 'recreative' || !empty($value);
},
'last' => true,
'message' => "If you don't fly recreatively, a license number needs to be supplied"
])
->allowEmpty('license_nr');
How do I make CakePHP run my custom function in order to see if the field can be empty, rather than just assuming that it can never be empty or must always be empty?
By default fields aren't allowed to be empty, so that's the expected behavior and you'll have to explicitly allow empty values.
In order to achieve what you're trying to do, instead of using an additional rule, you should customize the allowEmpty() rule, use the second argument to supply a callback (note that unlike rule callbacks it will receive a single argument that provides the context).
So you'd do something like this, which you may need to modify a bit, for example depending on whether you need it to work differently for creating ($context['newRecord'] = true) and updating ($context['newRecord'] = false) records:
->allowEmpty(
'license_nr',
function ($context) {
return $context['data']['pilot_type'] === 'recreative';
},
"If you don't fly recreatively, a license number needs to be supplied"
)
As of CakePHP 3.7 you can use allowEmptyString(), it will work the same way, you just need to swap the second and third arguments, as allowEmptyString() takes the message as the second argument, and the callback as the third argument.
See also
Cookbook > Validation > Conditional Validation

Laravel 5.4 different value validation rule in array

I have an input with an array of entities with an ID which must be unique
I've tried this:
'authors.*.id' => 'different:authors.*.id'
But it says 'The authors.0.id and authors.0.id must be different'
So what is a right way to validate this?
You want to use distinct rule.
When working with arrays, the field under validation must not have any duplicate values.
'foo.*.id' => 'distinct'

Apply several constraints in one "validate" pass in Silex

I'm using Silex, and trying to validate some value with validator service. But the problem is that I need apply several constraints to one value, but validator don't let to do this without using Required constraint.
When I just want to validate a choice value (say, 'apple' or 'orange') I have to wrap constraints Choice and NotNull (because Choice allows a null value) into Required and Collection (because Required cannot be used without Collection and it is CollectionValidator who validates series of Required's constraints) like this:
$violations = $app['validator']->validate(array('value'),
new Collection(array(
new Required(array(
new Choice(array('apple', 'orange')),
new NotNull()
))
)));
It looks verbose so I'm looking for more elegant solution (for such explicit use of validator).
You can use the validateValue function that accepts an array of constraints as second parameter.
$violations = $app['validator']->validateValue('value', array(
new Choice(array('apple', 'orange')),
new NotBlank()
));

Resources