Laravel multi dimensional array validation - laravel

I am trying to validate a multi dimensional array in Laravel. This whole input itself is not required, however, if it is present, all of its keys should have some value.
My Input has dynamic input arrays.
Example :
$users = [
['name' => 'John' , 'Age' => 25],
['name' => 'Nick' , 'Age' => 28]
]
My requirement is that, if a record is sent along with the request, it has to contain both name and age. At the same time, this whole array is not mandatory.
I can accept
$users = []
Cannot accept
$users = [
['name' => '' , 'Age' => 25],
['name' => 'Nick' , 'Age' => null]
]

Something like that in your Request should work:
public function rules()
{
return [
'users' => 'array', //add 'sometimes' if the array can be null other than empty
'users.*.name' => 'required',
'users.*.Age' => 'required',
]
}
This return false if any of the user is missing name or Age attributes.

I like #Shafeel's solution - Here is another way to go around:
if(count($users >= 1){
foreach($users as $user) {
if(empty($user->name) || empty($user->age) {
return false; //break the loop and return with error message
}
}
}

You can validate an array as an optional or sometimes a validation rule inside a validator.
public function rules()
{
return [
'users' => 'sometimes|array',
'users.*.name' => 'sometimes',
'users.*.Age' => 'sometimes',
]
}
** OR you can**
public function rules()
{
return [
'users' => 'optional|array',
'users.*.name' => 'optional',
'users.*.Age' => 'optional',
]
}

Related

Laravel validating that key in array is in an array of given strings

I'm building an API that takes in an array of 'additional_data' but I want some control over the fields that can be passed in.
Take the following JSON:
{
"name": "Joe Bloggs",
"additional_data": {
"type": "example",
"other_type": "example"
}
}
My current validation attempt:
return [
'name' => ['required'],
'additional_data.*' => ['sometimes', Rule::in(['type'])]
];
This always fails validation, what I'm looking for is to validate the key of the array so I can make sure the keys passed in are part of a 'whitelist'.
What you do now is you try to validate content of additional_data.type and additional_data.other_type.
You can do this by adding a custom validator. For example
Validator::extend('check_additional_data_keys', function($attribute, $value, $parameters, $validator) {
return is_array($value) && array_diff(array_keys($value), ['type', 'other_type']) === 0);
});
and use it inside your current rules
return [
'name' => ['required'],
'additional_data' => ['check_additional_data_keys'],
'additional_data.*' => ['required', 'string'],
];
Just specify your whitelist keys using the array validation rule:
return [
'name' => 'required',
'additional_data' => [
'sometimes',
'array:type',
],
];
1- In case you want applay same validation on all arrays' keys you can use the following:
return [
'name' => 'required',
'additional_data' => ['array', Rule::in(['type'])]
];
2- In case each key in the array needs different validation use the following:
return [
'name' => 'required',
'additional_data' => 'array',
'additional_data.ky1' => ['your validation here'],
'additional_data.ky2' => ['your validation here'],
];

Laravel Resource collection showing null field

I'm developing an API with Laravel. In one of the endpoint I'm accessing, some fields are showing a null value, but it should have some information.
Note the "addicionais_descricao" and "valor" fields, both always come with null values when I include them in the attributeitems array, but if I leave it at the initial level, the data is presented, but it doesn't solve my case, because I need this information with the attribute items:
enter image description here
This is where the endpoint calls, I make the query in the "Attribute" table, which has a relationship with the "Attributeitems" table, while the "attributeitems" table is linked to "Attribute" and "product".
public function show($id)
{
$atributos = Atributo::query('atributo')
->select(
'atributo.id',
'atributo.atrdescricao',
'atributoitens.atributo_id',
'atributoitens.produto_id',
'produto.prodescricao',
'produto.provalor'
)
->leftJoin('atributoitens', 'atributo.id', '=', 'atributoitens.atributo_id')
->leftJoin('produto', 'produto.id', '=', 'atributoitens.produto_id')
->where('atributo.id', '=', $id)
->get()->unique('id');
return AtributoResource::collection($atributos);
}
Resource Atributo:
public function toArray($request)
{
return [
'id' => $this->id,
'descricao' => $this->atrdescricao,
'atributoitens' => AtributoitensResource::collection($this->atributoitens),
];
}
Resource Atributo Itens:
public function toArray($request)
{
return [
'id' => $this->id,
'atributo' => $this->atributo_id,
'produtos' => $this->produto_id,
'adicionais_descricao' => $this->prodescricao,
'valor' => $this->provalor
];
}
What is the correct procedure for this situation?
Take this example as a reference :
Controller
$data = $shop->products()
->whereStatus(true)
->where('product_shop.active', true)
->where('product_shop.quantity', '>=', $this->min_product_qty)
->paginate(50);
return (new ProductCollection($data))
->response()
->setStatusCode(200);
ProductCollection
public function toArray($request)
{
return [
'data' => $this->collection
->map(function($product) use ($request) {
return (new ProductResource($product))->toArray($request);
}),
'brand' => $this->when($request->brand, $request->brand)
];
}
ProductResource
public function toArray($request)
{
return [
'type' => 'product',
'id' => (string) $this->id,
'attributes' => [
'uuid' => $this->uuid,
'name' => $this->name,
'slug' => $this->slug,
'description' => $this->description,
'thumb_path' => $this->thumb_path,
'cover_path' => $this->cover_path,
],
'relationships' => [
'brand' => $this->brand
]
];
}
Something like this should help you do what you want. I cant exactly do it for you. by the way why you are not using Eloquent, something like
Attribute::where(...)->with(['relation_1', 'products'])->get();
public function toArray($request)
{
return [
'id' => $this->id,
'attributes' => [...],
'products' => $this->collection
->map(function($this->product) use ($request) {
return (new ProductResource($product))->toArray($request);
}),
];
}

How to access nested item in Rule::requiredIf() validation

I'm trying to validate an array inside a custom Request. The rule evaluates to required if two conditions are met:
attribute3 is true
another column from the same array is true
Here's what I'm doing:
public function rules()
{
return [
'attribute1' => 'required',
'attribute2' => 'required',
'attribute3' => 'required',
...
'attribute10.*.column3' => Rule::requiredIf(fn() => $this->attribute3), // <- array
'attribute10.*.column4' => Rule::requiredIf(fn() => $this->attribute3), // <- array
'attribute10.*.column5' => Rule::requiredIf(fn() => $this->attribute3), // <- array
];
}
What I really need is this:
'attribute10.*.column4' => Rule::requiredIf(fn($item <- magically hint this currently looped item) => $this->attribute3 && $item->column2 <- so I can use it like this), // <- array
Assuming the incoming request has a structure like the following:
[
'attribute1' => 1,
'attribute2' => 0,
'attribute3' => 1,
'attribute10' => [
[
'column1' => 1,
'column2' => 1,
'column3' => 0,
],
[
'column1' => 0,
'column2' => 1,
'column3' => 0,
],
],
]
You can set the rules array to a variable, and then loop over the attribute10 field array elements and merge each rule on the rules variable. Then you'll have access to the other elements on the nested array.
Ie:
public function rules()
{
$rules = [
'attribute1' => 'required',
'attribute2' => 'required',
'attribute3' => 'required',
];
foreach($this->attribute10 as $key => $item) {
array_merge($rules, [
'attribute10.'.$key.'.column2' => Rule::requiredIf($this->attribute3 && $item['column1']),
'attribute10.'.$key.'.column3' => Rule::requiredIf($this->attribute3 && $item['column2']),
//...
]);
}
return $rules;
}

Laravel more dimesions array validation

There is a request.How I can validate it? I trying with foreach, but i does not work. Thanks.
The request:
'show_data' => [
0 => [
'buyer_search_property_id' => 1,
'date_of_show' => '2019-01-01',
'comment' => 'Nice flat',
],
1 => [
'buyer_search_property_id' => 2,
'date_of_show' => '2019-01-31',
'comment' => 'Too small',
], etc...
],
I tried this, but it does not work (of course... :))
public function rules()
{
$rules = [];
foreach ($this['show_data'] as $key => $item) {
$rules["show_data[$key]['buyer_search_property_id']"] = 'required';
$rules["show_data[$key]['date_of_show']"] = 'required|date';
$rules["show_data[$key]['comment']"] = 'required';
}
return $rules;
}
This is from laravel documentation:
You may also validate each element of an array. For example, to validate that each e-mail in a given array input field is unique, you may do the following:
$validator = Validator::make($request->all(), [
'person.*.email' => 'email|unique:users',
'person.*.first_name' => 'required_with:person.*.last_name',
]);
Read more here
You can validate array in laravel easily like this:
public function rules()
{
return [
"show_data.*.buyer_search_propery_id" => "required",
"show_data.*.date_of_show" => "required|date",
"show_data.*.comment" => "required",
];
}
if you want your method to work, do this:
public function rules()
{
$rules = [];
foreach ($this['show_data'] as $key => $item) {
$rules["show_data.$key.buyer_search_property_id"] = 'required';
$rules["show_data.key.date_of_show"] = 'required|date';
$rules["show_data.$key.comment"] = 'required';
}
return $rules;
}
just remove the inner square brackets and the quotes and add dots to the indices, so change $rules["show_data[$key]['date_of_show']"] to $rules["show_data.$key.date_of_show"]
$data['show_data'] = [
0 => [
'buyer_search_property_id' => 1,
'date_of_show' => '2019-01-01',
'comment' => 'Nice flat',
],
1 => [
'buyer_search_property_id' => 2,
'date_of_show' => '2019-01-31',
'comment' => 'Too small',
],
];
$rules = [
'show_data.*.buyer_search_property_id' => 'required',
'show_data.*.date_of_show' => 'required|date',
'show_data.*.comment' => 'required',
];
$validator = Validator::make($data, $rules);
Note asteriks at rules. With this you don't need a foreach loop. And it looks and feels a lot cleaner.

Attach extra attributes to Laravel pivot table

I have 4 extra attributes ('product_id', 'quantity', 'discount_percent', 'discount_amount') in my pivot table, but my values for these are always 0 when I store, while the rest is populated properly. Any ideas?
Invoice model
public function productversion()
{
return $this->belongsToMany('App\Productversion')->withPivot('product_id', 'quantity', 'discount_percent', 'discount_amount')->withTimestamps();
}
Productversion model
public function invoice()
{
return $this->belongsToMany('App\Invoice')->withPivot('product_id', 'quantity', 'discount_percent', 'discount_amount')->withTimestamps();
}
Controller (store)
$invoice->productversion()->attach($productversionid, ['product_id' => $productid], ['quantity' => $qty], ['discount_percent' => $discountprc], ['discount_amount' => $discountamt]);
Just one array will do:
$invoice->productversion()->attach($productversionid, [
'product_id' => $productid,
'quantity' => $qty,
'discount_percent' => $discountprc,
'discount_amount' => $discountamt
]);
In addition to #jeff's method, you can attach or sync multiple elements with pivot attributes providing a multidimensional array that has ids as keys.
$attach_data[$productversionid] = [
'product_id' => $productid,
'quantity' => $qty,
'discount_percent' => $discountprc,
'discount_amount' => $discountamt
];
Then you can $invoice->productversion()->attach($attach_data).
Substantially you can pass an array like
[
'relation_id' => [
// pivot data
],
'relation_id' => [
// pivot data
],
// ...
]

Resources