I have a small question concerning validation.
there is an api route POST /api/document/{document}/link it accepts an array of document IDs ({"ids": [1, 2, 3]}) to be linked to the Document bound to the route. I validate this array as follows
public function rules()
{
return [
'ids' => 'required|array',
'ids.*' => 'numeric|exists:documents,id'
];
}
The thing is the Document model has a partner attribute and it's not possible to link together documents from different partners. What I want is to check if the documents passed (by their IDs) belong to the same partner as the bound Document. I would like to validate this within the FormRequest. Is it possible?
You can use these for your rules:
'ids' => [
'required',
'array'
],
'ids.*' => [
'required',
'exists:documents,id'
],
'ids.*.partner_id' => [
Rule::in([$document->partner_id])
]
this wil validate your id matches with the numbers in the array, since we only put the id from the route given $document in there it should match or return failed.
So, here is what I ended up with:
public function rules()
{
/** #var Document $document */
$document = $this->route('document');
return [
'ids' => ['required', 'array'],
'ids.*' => ['required', 'numeric', Rule::exists('documents','id')->where('partner_id', $document->partner_id)],
];
}
As it turned out the case is described in Laravel docs here https://laravel.com/docs/5.8/validation#rule-exists. I just needed to customize the query executed to ensure that both the passed id and partner_id exist.
Related
Summary
Context
Sources
2.1. Unit test
2.2. FormRequest's rules method
Behaviors
3.1. Actual behavior
3.2. Expected behavior
Question
Context
In a Unit test, I want to send data to a FormRequest in a REST call. I am testing the behavior of the validation rules I've written in the rules method of the FormRequest.
Sources
Unit test
public function test_detach_user_job_status()
{
$response = $this->put(route('users.update', ['user' => $this->applier['id']], [
'job' => [
]
]));
$response->assertStatus(200);
}
FormRequest's rules method
public function rules()
{
return [
'name' => 'nullable|string',
'job' => 'nullable|array:id,attach_or_detach,message|required_array_keys:id,attach_or_detach',
'job.id' => 'integer|gt:0',
'job.attach_or_detach' => 'boolean',
'job.message' => 'required_if:job.attach_or_detach,true|string',
];
}
Behaviors
Actual behavior
The test succeeds.
Expected behavior
The test fails. Indeed, the array job is provided but no keys id or attach_or_detach or (eventually) message are provided, whereas the validation rules do specify: required_array_keys:id,attach_or_detach.
Also, if no job array is specified at all, then the validator must not reject the request because this array is not provided, nor its keys: it's perfectly normal since the array must be optional (it is nullable to provide this feature).
Question
Why doesn't Laravel make my test fail since my nullable (= optional) array is provided, and that its keys are required?
You didn't put the correct input. you should put the post body to put() method instead of route() method
change this:
$response = $this->put(route('users.update', ['user' => $this->applier['id']], [
'job' => [
]
]));
to:
$response = $this->put(route('users.update', ['user' => $this->applier['id']]),
[
'job' => []
]);
Laravel 5.7. I have a form request validation for a model Foo. The model has an optional field bar, which must be an array. If it is present, it must contain two keys, bing and bang. But if the array is absent, obviously these two keys should not be validated.
This is what I have so far:
return [
'bar' => 'bail|array|size:2',
'bar.bing' => 'required|numeric',
'bar.bang' => 'required|numeric',
];
This works when I send a request with the bar array present. But when I send a request without the bar array, I still get the validation errors
The bar.bing field is required
The bar.bang field is required
How can I make them only required when bar is present?
Try with this rules
return [
'bar' => 'nullable|bail|array|size:2',
'bar.bing' => 'required_with:bar|numeric',
'bar.bang' => 'required_with:bar|numeric',
]
Docs for required_with
Here's what I tend to do in this sort of situations
public function rules(): array
{
$rules = [
// ...
];
if ($this->bar) {
$rules['bar'] = 'array|size:2';
$rules['bar.bing'] = 'required|numeric';
$rules['bar.bang'] = 'required|numeric';
}
return $rules;
}
I want to amend rules in my API request validation. This request is to update a travel_experience model instance.
These are the current rules:
protected $rules = [
'city_id' => 'exists:cities,id',
'country_id' => 'exists:countries,id',
Basically I want to make city_id and country_id optional. Which means they might or might not exist in the request, if they exist, they cannot be null and must have an ID value for city or country.
In short, if they don't exist, then there value should remain the same in the DB.
From the documentation:
protected $rules = [
'city_id' => 'nullable|exists:cities,id',
'country_id' => 'nullable|exists:countries,id',
Depending on the version of Laravel you're using, you should be able to use the nullable validation rule:
protected $rules = [
'city_id' => 'nullable|exists:cities,id',
'country_id' => 'nullable|exists:countries,id',
I send an array to a REST API. How can I add a rule for the array?
Also I want to add field_name_id, field_input_type and field_caption as required fields.
I don't know how can I access the array in Laravel rules. Can someone help me?
$rules = [
'name' => 'required',
'forms' => 'array'
]
Laravel uses dot notation to validate arrays and it's nested fields.
$rules = [
'forms.field_name_id' => 'required',
'forms.field_input_type'=> 'required',
'forms.field_caption' => 'required',
]
You can also validate each value within the array. For example, If you want the caption to be unique:
$rules = [
'forms.*.field_caption' => 'unique:captions,caption',
]
Here are the docs for more information on how to use them
I'm using the dwightwatson/validating package to create validation rules in the model.
I particularly like the custom rulesets you can create for different routes.
Model
protected $rulesets = [
'set_up_all' => [
'headline' => 'required|max:100',
'description' => 'required'
],
'set_up_property' => [
'pets' => 'required'
],
'set_up_room' => [
'residents_gender' => 'required',
'residents_smoker' => 'required'
],
'set_up_roommate' => [
'personal_gender' => 'required',
'personal_smoker' => 'required'
]
];
Controller
$post = new Post(Input::all());
if($post->isValid('set_up_all', false)) {
return 'It passed validation';
} else {
return 'It failed validation';
}
In the above example, it works well in validating against the set_up_all ruleset. Now I would like to combine several rulesets and validate against all of them together.
According to the documentation, the package offers a way to merge rulesets. I just can't figure out how to integrate the example provided into my current flow.
According to the docs, I need to implement this line:
$mergedRules = $post->mergeRulesets('set_up_all', 'set_up_property_room', 'set_up_property');
This was my attempt, but it didn't work:
if($mergedRules->isValid()) { ...
I get the following error:
Call to a member function isValid() on array
I also tried this, but that didn't work either:
if($post->isValid($mergedRules)) { ...
I get the following error:
array_key_exists(): The first argument should be either a string or an integer
Any suggestions on how I would implement the merging rulesets?
From what I can see - mergeRulesets() returns an array of rules.
So if you do this - it might work:
$post = new Post(Input::all());
$post->setRules($post->mergeRulesets('set_up_all', 'set_up_property_room', 'set_up_property'));
if($post->isValid()) {
///
}
I've released an update version of the package for Laravel 4.2 (0.10.7) which now allows you to pass your rules to the isValid() method to validate against them.
$post->isValid($mergedRules);
The other answers will work, but this syntax is nicer (and won't override the existing rules on the model).