Laravel more dimesions array validation - laravel

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.

Related

How to validate a string in laravel

I have a textarea where a user can bulk add clients. In the textarea they would add the clients like this
client 1,client1#domain.com,client username 1
client 2,client2#domain.com,client username 2
client 3,client3#domain.com,client username 3
Here is what I have so far
public function bulkClients()
{
$bulk = request('bulk_clients');
$split = explode("\n",$bulk);
foreach($split as $row)
{
$split_row = explode(",", $row);
$name = $split_row[0];
$email = $split_row[1];
$username = $split_row[2];
$validate = Validator::make($email, [
$email => 'email',
$username => 'unique:App\User,username'
]);
if($validate->fails())
{
$messages = $validate->messages();
return response()->json([
'messages' => $messages
]);
}
}
}
What I would like to know is how can I validate that $email is an email or that $username is unique.
The first param of Validator::make should be an array and the second is the rules for the keys of the array.
public function bulkClients()
{
$bulk = request('bulk_clients');
$split = explode("\n",$bulk);
foreach($split as $row)
{
$split_row = explode(",", $row);
$client['name'] = $split_row[0];
$client['email'] = $split_row[1];
$client['username'] = $split_row[2];
$validate = Validator::make($client, [
'email' => 'email',
'username' => 'unique:App\User,username'
]);
if($validate->fails())
{
$messages = $validate->messages();
return response()->json([
'messages' => $messages
]);
}
}
}
You find all available rules in the docs:
$validate = Validator::make([ 'email' => $split_row[1],
'username' => $split_row[2]
], [
'email' => 'string',
'username' => 'unique:App\User,username'
]);
However, for email, I would rather check if its a valid email instead of a string. Thus,
'email' => 'email'
would be recommended.
I would suggest using the validator for the whole array instead of validating it row by row:
$clients = Str::of(request()->get('bulk_clients'))
->explode("\n")
->map(fn ($value) => Str::of($value)->explode(','))
->toArray();
$validator = Validator::make(compact('clients'), [
'clients' => 'array',
'clients.*.0' => 'required|string',
'clients.*.1' => 'required|email',
'clients.*.2' => 'required|unique:App\User,username',
]);

How to make a good validation on laravel?

I have data, they look like this:
{
sender_name : "Real fake sender name",
recipient_name : "Real fake recipient name",
goods: [
{
"no" : 1
"name":"Pen",
"unit": "1",
"qty":"50",
"price":"50",
"amount":"2500",
"vat_percent":"5",
"vat_sum": "125",
"total_sum": "2625"
}
]
}
I need to validate "goods" using extend validator. Here is his code:
Validator::extend('invoiceGoods' , function($attribute, $value, $parameters, $validator) {
$rulesForGoods = [
'no' => 'integer|required',
'name' => 'string|max:64|required',
'unit' => 'required|integer',
'qty' => 'required|string',
'price' => 'required|numeric',
'amount' => 'required|numeric',
'vat_percent' => 'nullable|numeric',
'vat_sum' => 'nullable|numeric',
'total_sum' => 'required|numeric'
];
foreach ($value as $good) {
$validator = Validator::make($good , $rulesForGoods);
if ($validator->fails()) {
return false;
}
}
return true;
});
This is the main code.
$validator = Validator::make($data , [
'goods' => 'invoiceGoods',
'sender_name' => 'string',
'recipient_name' => 'string',
]);
if ($validator->fails()) {
return response()->json([
'success' => false,
'message' => 'Validation error.',
'data' => $validator->errors()
]);
}
If the goods validation error occurs, I get this answer:
But I would like to display errors like this: the wrong unit in the goods with no 1.
I know that the third argument can be passed an array with custom messages, but how to return it from the extended validator if it should return true or false?
https://laravel.com/docs/5.8/validation#custom-error-messages
$messages = [
'Validation.invoice_goods' => 'Errror message!',];
$validator = Validator::make($input, $rules, $messages);

Using isDirty in Laravel

I'm using the isDirty() method in my controller to check if any field is changed. Then I am saving the old data of a field and the new data in a table. The code is working fine; however, how can I optimize this code?
By using the below code, I will have to write each field name again and again. If request->all() has 20 fields, but I want to check six fields if they are modified, how can I pass only 6 fields in the below code, without repeating?
Controller
if ($teacher->isDirty('field1')) {
$new_data = $teacher->field1;
$old_data = $teacher->getOriginal('field1');
DB::table('teacher_logs')->insert(
[
'user_id' => $user->id,
'teacher_id' => $teacher->id,
'old_value' => $old_data,
'new_value' => $new_data,
'column_changed' => "First Name",
]);
}
You can set a list of what fields you want to be checking for then you can loop through the dirty fields and build your insert records.
use Illuminate\Support\Arr;
...
$fields = [
'field1' => 'First Name',
'field2' => '...',
...
];
$dirtied = Arr::only($teacher->getDirty(), array_keys($fields));
$inserts = [];
foreach ($dirtied as $key => $value) {
$inserts[] = [
'user_id' => $user->id,
'teacher_id' => $teacher->id,
'old_value' => $teacher->getOriginal($key),
'new_value' => $value,
'column_changed' => $fields[$key];
];
}
DB::table(...)->insert($inserts);
i tried following code after getting idea by lagbox in comments, and i have found solution to my problem.
$dirty = $teacher->getDirty('field1','field2','field3');
foreach ($dirty as $field => $newdata)
{
$olddata = $teacher->getOriginal($field);
if ($olddata != $newdata)
{
DB::table('teacher_logs')->insert(
['user_id' => $user->id,
'teacher_id' => $teacher->id,
'old_value' => $olddata,
'new_value' => $newdata,
'column_changed' => "changed",
]);
}
}

Laravel multi dimensional array validation

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',
]
}

Result by model

I have this query:
$data = TableRegistry::get('Dogs');
$data = $data->find('all')
->contain(['Foods'])
->select(['name','breed','sex','Foods.name','Foods.quality'])
->last()
->toArray();
It returns this result:
[
'name' => 'Dug',
'breed' => 'Golden Retriever',
'sex' => 'Male',
'food' => [
'name' => 'Best Food',
'quality' => 'A+'
]
]
I want to know if there is any way to make CakePHP return the result as:
[
'dog' => [
'name' => 'Dug',
'breed' => 'Golden Retriever',
'sex' => 'Male'
],
'food' => [
'name' => 'Best Food',
'quality' => 'A+'
]
]
How would I do this? I am pretty sure it's very simple, but I can't figure it out.
Maybe like this:
$data => [
'dog' => [
'name' => $dog->name,
'breed' => $dog->breed,
'sex' => $dog->sex
],
'food' => $dog->foods
];
$this->set('data', $data);
So following Salines' post, I decided to just build something quick and easy to work with since the content is a bit more dynamic than the example I posted.
$data = TableRegistry::get($this->table);
$data = $data->find()
->contain($associated)
->select($this->data_fields)
->last()
->toArray();
$main = strtolower(Inflector::singularize($this->table));
$data[$main] = [];
foreach ($data as $key => $value) {
if(!is_array($value)) {
$data[$main][$key] = $value;
unset($data[$key]);
}
}

Resources