Laravel array sum of fields validation - laravel

I'm new to Laravel where I'm using Laravel's validator for a project not built in Laravel.
I need to know if there is a simple built in Laravel validator to validate the sum of a certain field in all of the objects in an array.
My input looks something like:
{
"customer":95,
"name": "My object",
"values":
[
{
"name": "AAA",
"percentage": 50
},
{
"name": "BBB",
"percentage": 50
}
]
}
I need to make sure that the sum of my percentages is 100. Is there a simple way?

Use Custom Validation Rules for single attribute validations.
Use After Validation Hook for other or more complex validations, say the sum of multiple fields.
public function withValidator($validator)
{
$validator->after(function ($validator) {
if ($this->get('field1') + $this->get('field2') + $this->get('field3') != 100) {
$validator->errors()->add(null, 'The sum of field1, field2 and field3 must be 100');
}
});
}

I think it would be best for you to create a custom validation rule. In the validation, I'd convert the values array to a collection and use the collection sum method. E.g.:
public function passes($attribute, $value)
{
$values = collect($value);
return $values->sum('percentage') <= 100;
}

Related

Laravel attribute not working as expected

In the current application, there is a model Part and a model Supplier. Every Part has a Supplier.
public function supplier()
{
return $this->belongsTo(Supplier::class);
}
I added an accessor to get the name of the supplier.
If i return the whole dataset, i get the supplier:
public function getSupplierNameAttribute()
{
return $this->supplier;
}
"supplierName": {
"id": 1,
"uuid": "37e3a715-09d3-4fac-ae88-8f12e63fe79c",
"name": "Laserteam",
"street": "8602 Dessie Tunnel",
"zip": "15869",
"city": "New Clementview",
"email_send_type": null,
"active": 0,
"created_at": "2020-01-09 09:46:02",
"updated_at": "2020-01-09 09:46:02",
"deleted_at": null,
"action": "",
"activeLabel": "<span class='badge badge-secondary'>Inaktiv<\/span>"
},
If i try to get only the name (what I need finally), there is an error:
public function getSupplierNameAttribute()
{
return $this->supplier->name;
}
ErrorException: Trying to get property 'name' of non-object in file /gopanel/sites/7industry_net/public/7time/app/Models/Part/PartAttribute.php on line 48
If i try in this way, it works:
public function getSupplierNameAttribute()
{
return $this->supplier['name'];
}
Why does return $this->supplier->name; not work?
It's hard to say what could be the problem here, but:
Make sure you don't have supplier in casts for your model
Make sure you don't use supplier for database column name or you don't make somewhere something this
$this->supplier = $this->supplier->toArray();
It seems supplier property somewhere becomes array that's why one notation works but the other doesn't.
I’m not really sure what’s going on there; if you have a supplier relation then accessing the property should lazy-load the relationship.
Alternatively, you could try this syntax:
public function getSupplierNameAttribute()
{
$this->loadMissing('supplier');
return $this->getRelation('supplier')->name;
}
However, I’d avoid defining accessors like this. It can lead to N+1 problems if you say, retrieve a collection of parts and then call $part->supplier_name on each one without eager-loading the supplier relationship.
Personally, if I’m accessing attributing on relations that I prefer to do it through the relation (i.e. $part->supplier->name) so any relations I need to eager load are shown to me.
Try this code
public function getSupplierNameAttribute()
{
return $this->supplier->name ?? 'supplier not exists';
}

How to get random row in object variable in Laravel?

Sorry for my bad english, I want to get a single row in my object. And I want that in random order. Im using array_rand() and it only return errors as stated below:
ErrorException: array_rand() expects parameter 1 to be array, object given in file C:\xampp\htdocs\user\TestProject\app\Http\Controllers\TestController.php on line
Here is my object.
"my_list": [
{
"id": 1,
"name": "My Name Test",
"address": [
{
"id": 1,
"city": "Manila",
"country": "Philippines"
}
]
},
{
"id": 2,
"name": "Your Name Test",
"address": [
{
"id": 2,
"city": "Cebu",
"country": "Philippines",
}
]
}
]
The problem is I want only to get a single row to the my_list which is object and not an array.
Here is my code.
$course = Course::where('id', 1)->with('my_list')->first();
$random_list = array_rand($course->my_list);
return $random_list;
I also try adding number of row in the array_rand like this.
$random_list = array_rand($course->my_list, 1);
But still not working.
What did I missed?
Any Eloquent query returns, by default, a Collection, even for the underlying relationships. Since you are working with one, this should work:
$course->my_list->random();
This will return only one item. If you want more, you could pass an argument to the random() method specifying the count of items you want.
For more information, check the documentation.
This Object is a Laravel collection. Please refer to the collection documentation.
https://laravel.com/docs/5.7/collections#method-random
You can try $course->my_list->random()
If you still wanna do this with your approach, can you try get_object_vars function to cast object into array.
$array = get_object_vars($object);
so that you can use them as an array in array_rand.
You might get an error, hence that it's an multi-dimensional array. Let me know so i may update.
Update for multidimensional:
Please refer to this.
// The second parameter of json_decode forces parsing into an associative array
$array = json_decode(json_encode($object), true);
try this:
$course = Course::where('id', 1)
->with(['my_list' => function($query) {
$query->inRandomOrder();
}])->first();
return $course->my_list;
Try this method:
$course = Course::where('id', 1)
->with(['my_list' => function($query) {
$query->inRandomOrder()->first();
}])->first();
return $course->my_list;
this method is more efficient since you will only get 1 row from my_list not like when you use $course->my_list->random() which retrieves all data and from there select a random row.
$random_list = $course['my_list']->random(number);
ps: number = number of element you want to get ,

Laravel overridden/appended date attributes aren't serialized in json output

I have an Organisation model that has a two data attributes, allowance_starts_at and allowance_ends_at, when returning this model as JSON I get the following back:
allowance_starts_at: "2017-06-01 00:00:00",
allowance_ends_at: "2018-05-31 23:59:59",
However I want to override these attributes to set the year to be the current year (or allowance period) so come 2018-06-01 I want the attributes to return:
allowance_starts_at: "2018-06-01 00:00:00",
allowance_ends_at: "2019-05-31 23:59:59",
To achieve this I'm overriding the attributes in the model like so:
public function getAllowanceStartsAtAttribute($value)
{
$currentYear = Carbon::now()->format('Y');
$d = Carbon::createFromFormat('Y-m-d H:i:s', $value);
$d->year($currentYear);
return $d;
}
public function getAllowanceEndsAtAttribute($value)
{
$d = Carbon::createFromFormat('Y-m-d H:i:s', $value);
if ($d->copy()->subYear()->addSecond() != $this->allowance_starts_at) {
$d->addYear();
}
return $d;
}
This works and gives me the result I want however, now when I output the model as JSON the Carbon date isn't serialised:
allowance_starts_at: {
date: "2017-01-01 00:00:00.000000",
timezone_type: 3,
timezone: "Europe/London"
},
allowance_ends_at: {
date: "2017-12-31 23:59:59.000000",
timezone_type: 3,
timezone: "Europe/London"
},
I've also tested this with appended attributes, so instead of overriding an existing attribute I create an appended attribute that just returned Carbon::now() and set the appended attribute in the $dates array and this outputs in the same way i.e. not serialized.
Does anyone now how Laravel determines how to output the attributes e.g. as Carbon or as a string?
Does anyone know how I can make this work?
Update
Thanks Paul. Using a setter mutator gave me the behaviour that I needed. I replaced my overriding methods with:
public function setAllowanceStartsAtAttribute($value)
{
$currentYear = Carbon::now()->format('Y');
$value->year($currentYear);
$this->attributes['allowance_starts_at'] = $value;
}
public function setAllowanceEndsAtAttribute($value)
{
if ($value->copy()->subYear()->addSecond() != $this->attributes['allowance_starts_at']) {
$value->addYear();
}
$this->attributes['allowance_ends_at'] = $value;
}
And now I get this output when returning JSON:
allowance_starts_at: "2018-06-01 00:00:00",
allowance_ends_at: "2019-05-31 23:59:59",
Have you tried using a mutator? Like instead of getAllowanceEndsAtAttribute, make it setAllowanceEndsAtAttribute?

Laravel - How to merge relationship results?

how can i merge laravel relationship results in to one object?
MyController.php
public function getUser($id) {
return TournamentUser::where('customer_id','=', $id)->with('user')->get();
}
MyModel.php
public function user() {
return $this->hasOne('App\Models\Customer','customer_id', 'customer_id');
}
returning result :
[{
"id":1,
"tournament_id":3,
"customer_id":2016827550,
"point":0,
"user":{
"id":1,
"customer_id":2016827550,
"nickname":"OMahoooo"
}
}]
as expected query returns user section as array but i expect result like this :
[{
"id":1,
"tournament_id":3,
"customer_id":2016827550,
"point":0,
"nickname":"OMahoooo"
}]
only get nickname section without user array. I hope you understand me.
Sorry for my english , have a nice day
You can load the data and remap it manually using map() method. I've just tested this code and it works perfectly with hasOne() relationship:
$users = TournamentUser::where('customer_id', $id)->with('user')->get();
$users->map(function ($i) {
$i->nickname = $i->user->nickname; // Set related nickname.
unset($i->user); // Delete user data from the collection.
return $i;
});
Alternatively, you could use an accessor, but in this case, you'll meet the N+1 problem.

Hide specific columns from pivot key

I have 2 tables say abc and xyz with ManyToMany relationship built over another table say abc_xyz (whose data will be returned as pivot key). However, pivot key upon retrieval has abc_id and xyz_id in return. I am able to access other columns in from abc_xyz table using method withPivot('dummy')
But, I want to hide the abc_id and xyz_id from the response. How do I do that?
I can hide the entire pivot key by using $hidden array but I want to hide only specific columns not the entire key.
Current Response
{
"abc_uuid": "some uuid",
"xyz" : [
{
"xyz_uuid": "some uuid",
"pivot": {
"abc_id": 1,
"xyz_id": 1,
"dummy" : "dummy value"
}
},
{
"xyz_uuid": "some uuid",
"pivot": {
"abc_id": 1,
"xyz_id": 2,
"dummy" : "dummy value"
}
}
]
}
So, I need only dummy from the pivot key, and hide abc_id and xyz_id. How do I do that?
Found a crude way to get this done. Found this answer in laravel issues unable to find the link now. However, it asks me to just add a method in my model and unset the keys I do not want.
public function toArray()
{
$attributes = $this->attributesToArray();
$attributes = array_merge($attributes, $this->relationsToArray());
foreach($attributes['xyz'] as $key => $value) {
unset($value['pivot']['abc_id']);
unset($value['pivot']['xyz_id']);
$attributes['xyz'][$key] = $value;
}
return $attributes;
}
this unsets the unwanted keys from my response. I hope laravel gives out an easy way for this.

Resources