How to manually map collection using laravel/php and get total amount - php

I currently have the below json response that the API is returning. I am able to return it using Laravel Eloquent. There are several users and each user has a several receipts. A receipt has types and status. I want to try to get the total sum amount for each receipt that is related to its type and status. I was able to return the below json response using
$this->user->with('receipts')->has('receipts')->get(['id', 'name']);
I have tried using multiple laravel collections methods https://laravel.com/docs/5.8/collections#available-methods But I am still unable to get the desired response.
[
{
"id": 1,
"name": "kent",
"receipts": [
{
"id": 1,
"user_id": 1,
"type_id": 1,
"status": 0,
"amount": 100
},
{
"id": 2,
"user_id": 1,
"type_id": 1,
"status": 0,
"amount": 100
},
{
"id": 3,
"user_id": 1,
"type_id": 2,
"status": 1,
"amount": 50
},
{
"id": 4,
"user_id": 1,
"type_id": 2,
"status": 0,
"amount": 30
},
{
"id": 5,
"user_id": 1,
"type_id": 2,
"status": 0,
"amount": 30
},
{
"id": 6,
"user_id": 1,
"type_id": 1,
"status": 0,
"amount": 20
},
{
"id": 7,
"user_id": 1,
"type_id": 1,
"status": 1,
"amount": 10
}
]
},
{
"id": 2,
"name": "allison",
"receipts": [
{
"id": 9,
"user_id": 2,
"type_id": 1,
"status": 0,
"amount": 20
}
]
}
]
I expect to get this:
[
{
"id": 1,
"name": "kent",
"receipts": [
{
"performance and deleted": 220,
"performance and not deleted": 10,
"project and deleted": 60,
"project and deleted": 50
}
]
},
{
"id": 2,
"name": "allison",
"receipts": [
{
"performance and deleted": 20,
"performance and not deleted": 0,
"project and deleted": 0,
"project and not deleted": 0
}
]
}
]
My main concern is to use laravel collection methods and easy to read code to get my expected result

In this way, I believe you get your expected result with nice readable code.
I'll assume that you have $users variable which contains a list of users in collection (And probably the receipts are already loaded).
// You need to name the keys as you desire. (I can't figure out the labels by the type_id and status numbers).
$statusPair = collect([
'performance and deleted' => [
'type_id' => 1,
'status' => 0,
],
'something' => [
'type_id' => 1,
'status' => 1,
],
'something 2' => [
'type_id' => 2,
'status' => 0,
],
'something 3' => [
'type_id' => 2,
'status' => 1,
],
]);
$data = $users->map(function ($user) use ($statusPair) {
$receipts = $user->receipts;
$sums = $statusPair->mapWithKeys(function ($pair, $statusLabel) use ($receipts) {
$filtered = $receipts;
foreach ($pair as $key => $value) {
$filtered = $filtered->where($key, $value);
}
$sum = $filtered->sum('amount');
return [
$statusLabel => $sum,
];
});
return [
'id' => $user->id,
'name' => $user->name,
'receipts' => $sums->toArray(),
];
});

i think this will help
$user->receipts->sum('amount');

Related

How to modify nested Laravel collection using group by

I want to modify nested collection using group by.
This is sample collection
"document": [
{
"id": 1,
"company_id": 4,
"client_id": 1,
"status": 1,
"client": {
"id": 1,
"company_id": 4,
"name": "1663159185735-client"
},
"document_items": [
{
"id": 1,
"master_id": 5,
"text_value": "piyo",
},
{
"id": 2,
"master_id": 5,
"text_value": "fuga",
},
{
"id": 3,
"master_id": 3,
"text_value": "hoge",
}
]
}
]
I want to change like this.
"document": [
{
"id": 1,
"company_id": 4,
"client_id": 1,
"status": 1,
"client": {
"id": 1,
"company_id": 4,
"name": "1663159185735-client"
},
"document_items": [
5: [{
"id": 1,
"master_id": 5,
"text_value": "piyo",
},
{
"id": 2,
"master_id": 5,
"text_value": "fuga",
}
],
3: [{
"id": 2,
"master_id": 5,
"text_value": "fuga",
       }
     ]
]
}
]
I try write below code;
$result->map(function ($v){
$v->documentItems = $v->documentItems->groupBy('master_id');
return $v;
});
but output key is documentItems not document_items
I changed to
$v->document_items = $v->documentItems->groupBy('master_id');
key is drawing_drawing_items but not groupby(simple array)
how to modify group by and preserve key case?
I changed to
$v->document_items = $v->documentItems->groupBy('master_id');
key is drawing_drawing_items but not groupby(simple array)
You should also change like this $v->document_items->groupBy('master_id')
The key is 'document_items' not 'documentItems'

add values ​from a collection to others using a column as a reference (PHP, LARAVEL)

I am working with php in laravel, in this case I have 2 collections of objects, one of them looks like this:
[
{
"id": 1,
"name": "product x",
"quantity": "100",
},
{
"id": 2,
"codProd": "product y",
"quantity": "200",
},
{
"id": 3,
"name": "product a.",
"quantity": "30",
}
]
and the other looks like this:
[
{
"reference": 1,
"quantity": "80",
},
{
"reference": 2,
"quantity": "50",
},
]
What I need is to keep the first collection but adding the value of the quantity key from the second collection, using the reference key as a relationship with the id of the first collection, the final result should look like this:
[
{
"id": 1,
"name": "product x",
"quantity": "180",
},
{
"id": 2,
"codProd": "product y",
"quantity": "250",
},
{
"id": 3,
"name": "product a.",
"quantity": "30",
}
]
so how can I do this?, any guide or help I am grateful
You can use a collection method for this, the map method:
$collection_one = collect([
[
'id' => 1,
'name' => 'product x',
'quantity' => 100,
],
[
'id' => 2,
'name' => 'product y',
'quantity' => 100,
],
[
'id' => 1,
'name' => 'product a.',
'quantity' => 30,
],
]);
$collection_two = collect([
[
'reference' => 1,
'quantity' => 80,
],
[
'reference' => 2,
'quantity' => 50,
],
]);
For you to get the collection what you want:
$collect = [];
$collect = $collection_one->map(function ($value_one) use ($collection_two) {
if ($search = $collection_two->firstWhere('reference', '=', $value_one['id'])) {
$value_one['quantity'] += $search['quantity'];
}
return $value_one;
});
Ready, that worked for me. :)

Validating array of relative arrays(maps) with Respect/Validation?

I'm trying to validate to following json:
{
"variants": [
{
"variant_code": "1",
"price": 12,
"discount": 12,
"height": 1,
"longitude": 1,
"width": 1,
"weight": 1,
"package_height": 1,
"package_longitude": 1,
"package_width": 1,
"package_weight": 11,
"stock": 1
},
{
"variant_code": "2",
"price": 12,
"discount": 12,
"height": 1,
"longitude": 1,
"width": 1,
"weight": 1,
"package_height": 1,
"package_longitude": 1,
"package_width": 1,
"package_weight": 11,
"stock": 1
},
{
"variant_code": "3",
"price": 12,
"discount": 12,
"height": 1,
"longitude": 1,
"width": 1,
"weight": 1,
"package_height": 1,
"package_longitude": 1,
"package_width": 1,
"package_weight": 11,
"stock": 1
}
]
}
I can't find a way to validate it, I've tried to following methods:
$this->validator->validate(
$request, [
"variants" => v::arrayVal()->each(
v::key("variant_code", v::stringVal()->notEmpty()->length(1, 100)),
v::key("stock", v::intVal()->notOptional()),
v::key("price", v::numericVal()->notEmpty()),
v::key("discount", v::numericVal()->notEmpty()),
v::key("weight", v::numericVal()->notEmpty()),
v::key("width", v::numericVal()->notEmpty()),
v::key("height", v::numericVal()->notEmpty()),
v::key("longitude", v::numericVal()->notEmpty()),
v::key("package_weight", v::numericVal()->notEmpty()),
v::key("package_width", v::numericVal()->notEmpty()),
v::key("package_longitude", v::numericVal()->notEmpty()),
v::key("package_height", v::numericVal()->notEmpty())
)
]
);
But it just validates the first key "variant_code" of each relative array.
I also tried this:
$this->validator->validate(
$request, [
"variants" => v::arrayVal()->each(
v::keySet(
v::key("variant_code", v::stringVal()->notEmpty()->length(1, 100)),
v::key("stock", v::intVal()->notOptional()),
v::key("price", v::numericVal()->notEmpty()),
v::key("discount", v::numericVal()->notEmpty()),
v::key("weight", v::numericVal()->notEmpty()),
v::key("width", v::numericVal()->notEmpty()),
v::key("height", v::numericVal()->notEmpty()),
v::key("longitude", v::numericVal()->notEmpty()),
v::key("package_weight", v::numericVal()->notEmpty()),
v::key("package_width", v::numericVal()->notEmpty()),
v::key("package_longitude", v::numericVal()->notEmpty()),
v::key("package_height", v::numericVal()->notEmpty())
)
)
]
);
But it throws the following error:
{
"variants": {
"variants": "Must have keys `{ \"variant_code\", \"stock\", \"price\", \"discount\", ... }`"
}
I've also tryied many other ways unsuccessfully.
I'm working with Respect/Validation version 2.0 and PHP version 7.4.
Anyone knows how to do it, with respect/validation?(I already know how to do it manually). Thank you.
The problem here is the product_image_id key that is required by your rules but it's not present in the data you're trying to validate.
What I tried:
$string = file_get_contents(__DIR__ . "./data.json");
$json = json_decode($string, true);
function validate($data) {
v::arrayVal()->each(
v::keySet(
v::key("variant_code", v::stringVal()->notEmpty()->length(1, 100)),
v::key("product_image_id", v::optional(v::intVal())), // This key is missing in the data.json file
v::key("stock", v::intVal()->notOptional()),
v::key("price", v::numericVal()->notEmpty()),
v::key("discount", v::numericVal()->notEmpty()),
v::key("weight", v::numericVal()->notEmpty()),
v::key("width", v::numericVal()->notEmpty()),
v::key("height", v::numericVal()->notEmpty()),
v::key("longitude", v::numericVal()->notEmpty()),
v::key("package_weight", v::numericVal()->notEmpty()),
v::key("package_width", v::numericVal()->notEmpty()),
v::key("package_longitude", v::numericVal()->notEmpty()),
v::key("package_height", v::numericVal()->notEmpty())
)
)->assert($data);
}
try {
/*
$array = [
[
"variant_code" => "qwerty"
]
];
$result = validate($array);
*/
validate($json["variants"]);
echo ("Validation ok");
} catch(NestedValidationException $exception) {
echo($exception->getFullMessage());
echo ("Validation error");
}
data.json file:
{
"variants": [
{
"variant_code": "1",
"price": 12,
"discount": 12,
"height": 1,
"longitude": 1,
"width": 1,
"weight": 1,
"package_height": 1,
"package_longitude": 1,
"package_width": 1,
"package_weight": 11,
"stock": 1
},
{
"variant_code": "2",
"price": 12,
"discount": 12,
"height": 1,
"longitude": 1,
"width": 1,
"weight": 1,
"package_height": 1,
"package_longitude": 1,
"package_width": 1,
"package_weight": 11,
"stock": 1
},
{
"variant_code": "3",
"price": 12,
"discount": 12,
"height": 1,
"longitude": 1,
"width": 1,
"weight": 1,
"package_height": 1,
"package_longitude": 1,
"package_width": 1,
"package_weight": 11,
"stock": 1
}
]
}
You can check my complete code here.

How to calculate the total amount for similar keys in PHP using Laravel eloquent/collections

I currently have the below json response that the API is returning. I am able to return it using Laravel Eloquent. There are several users and each user has a several receipts. A receipt has types and status. I want to try to get the total sum amount for each receipt that is related to its type and status. I was able to return the below json response using
$this->user->with('receipts')->has('receipts')->get(['id', 'name']);
I have tried using multiple laravel collections methods https://laravel.com/docs/5.8/collections#available-methods
But I am still unable to get the desired response.
{
"id": 1,
"name": "kent",
"receipts": [
{
"id": 1,
"user_id": 1,
"type_id": 1,
"status": 0,
"amount": 100
},
{
"id": 2,
"user_id": 1,
"type_id": 1,
"status": 0,
"amount": 100
},
{
"id": 3,
"user_id": 1,
"type_id": 2,
"status": 1,
"amount": 50
},
{
"id": 4,
"user_id": 1,
"type_id": 2,
"status": 0,
"amount": 30
},
{
"id": 5,
"user_id": 1,
"type_id": 2,
"status": 0,
"amount": 30
},
{
"id": 6,
"user_id": 1,
"type_id": 1,
"status": 0,
"amount": 20
},
{
"id": 7,
"user_id": 1,
"type_id": 1,
"status": 1,
"amount": 10
}
]
},
{
"id": 2,
"name": "allison",
"receipts": [
{
"id": 9,
"user_id": 2,
"type_id": 1,
"status": 0,
"amount": 20
}
]
}
]
I expect to get the below
{
"id": 1,
"name": "kent",
"receipts": [
{
"performance and deleted": 220,
"performance and not deleted": 10,
"project and deleted": 60,
"project and deleted": 50
}
]
},
{
"id": 2,
"name": "allison",
"receipts": [
{
"performance and deleted": 20,
"performance and not deleted": 0,
"project and deleted": 0,
"project and not deleted": 0
}
]
}
]
You should be able to get the sum of amount with
$this->user->with(['receipts' => function($query) {
$query->selectRaw("SUM(amount) as amount, type_id, status, user_id")->groupBy('type_id', 'status', 'user_id');
}])->has('receipts')->get(['id', 'name']);
You can use collection methods to get the desired output
$this->user->with(['receipts' => function($query) {
$query->selectRaw("SUM(amount) as amount, type_id, status, user_id")->groupBy('type_id', 'status', 'user_id');
}])->has('receipts')->get(['id', 'name'])
->each(function ($user) {
$user->setRelation(
'receipts',
$user->receipts->mapWithKeys(function ($receipt) {
return [
$receipt->type_id . ' and ' . $receipt->status => $receipt->amount // format the key as you wish
];
})
);
})
You may use foreach and other loops to make the json you want!
use This function to convert your collection to the json you want :
public function convert(Collection $collection)
{
$result_collection = $collection;
$payment_collections = [];
foreach ($result_collection->receipts as $receipt)
{
$payment["type_id${receipt->type_id} and status${$receipt->status}"] = $receipt->amount;
}
$result_collection->receipts = $payment_collections;
return $result_collection;
}
And This is same way to get total amount. Just put an foreach and add each amount to a variable that initialized with 0;
and there is other ways like change toArray function in your Resource Collection.
I wrote the script assuming your data is a PHP array so you may change some part of my code.
For example you may change:
$row['receipts']
// To
$row->receipts
anyway
// The function in your controller
function index(){
$users=User::whereHas('receipts')->with('receipts')->get()->toArray();
return convert($users);
}
// The helper function
function convert($data){
$data=collect($data);
$allTypes=[];
$allStatuses=[];
return $data->each(function($row) use (&$allTypes,&$allStatuses){
$types=collect($row['receipts'])->pluck('type_id')->toArray();
$statuses=collect($row['receipts'])->pluck('status')->toArray();
$allTypes=array_unique(array_merge($allTypes,$types));
$allStatuses=array_unique(array_merge($allStatuses,$statuses));
})->map(function ($row,$index) use (&$allTypes,&$allStatuses){
$result=[];
$receipts=collect($row['receipts']);
foreach ($allTypes as $type){
foreach ($allStatuses as $status){
$result["type_id {$type} and status {$status}: "]=$receipts->where('type_id',$type)->where('status',$status)->sum('amount');
}
}
$row['receipts']=$result;
return $row;
});
}

API Resources laravel filter data

So i am trying to get some data pass to an array and send to json.
But when i do this i am getting alot of information:
This is the response:
{
"current_page": 1,
"data": [{
"id": 2,
"object_id": 3,
"booking_id": 1,
"sender_id": 1,
"receiver_id": 2,
"message": "It is accepted",
"type_id": 5,
"read": 1,
"archive": 0,
"star": 0,
"created_at": "2019-02-26 11:45:28",
"updated_at": "2019-02-26 12:15:11",
"created_time": "26\/02\/2019",
"host_user": 0,
"guest_user": 1,
"sender": {
"id": 1,
"first_name": "Joe",
"last_name": "Cos",
"email": "email-91#hotmail.com",
"profile_image": null,
"balance": 0,
"status": "Active",
"created_at": "2019-02-21 15:19:26",
"updated_at": "2019-02-21 15:19:26",
"profile_src": "http:\/\/xxx.com\/public\/images\/user_pic-225x225.png"
},
"bookings": {
"id": 1,
"object_id": 3,
"code": "mYuL4p",
"host_id": 1,
"user_id": 2,
"start_date": "2019-02-26",
"end_date": "2019-02-27",
"status": "Accepted",
"guest": 0,
"total_night": 1,
"per_night": 20,
"base_price": 20,
"cleaning_charge": 0,
"guest_charge": 0,
"service_charge": 0,
"security_money": 0,
"host_fee": 0,
"total": 20,
"booking_type": "request",
"currency_code": "EUR",
"cancellation": "Flexible",
"transaction_id": "67427302T32774838",
"payment_method_id": 1,
"accepted_at": "2019-02-26 11:45:28",
"expired_at": null,
"declined_at": null,
"cancelled_at": null,
"cancelled_by": null,
"created_at": "2019-02-26 11:37:36",
"updated_at": "2019-02-26 11:45:28",
"host_payout": 23,
"label_color": "success",
"date_range": "Feb 26 - 27, 2019",
"expiration_time": "2019\/02\/27 11:37:36",
"currency": {
"id": 3,
"name": "Europe",
"code": "EUR",
"symbol": "€",
"rate": "0.88",
"status": "Active",
"default": "0",
"org_symbol": "€"
}
},
"object_address": {
"id": 3,
"object_id": 3,
"address_line_1": "XXXXXXXXX, 4050-352 Porto, Portugal",
"address_line_2": null,
"latitude": "49.999",
"longitude": "-8.88810419921",
"city": "P",
"state": "P",
"country": "P",
"postal_code": "4050-352"
}
}],
"from": 1,
"last_page": 1,
"next_page_url": null,
"per_page": 10,
"prev_page_url": null,
"to": 1,
"total": 1
}
I am using the API Resources
In MY Resource i have:
return parent::toArray($request);
With this i get all that information my problem is i need to filter some data
and when i try to filter for exemple the email.
return [
'email'=> $this->email,
];
it gives error and says
"Property [email] does not exist on this collection instance."
I am learning and i am a newbie i am sorry for this dumb questions.
Thanks to all!
First make a trait
trait ResourceHelpers
{
/**
* Remove null values from Eloquent api resource
* #param array $data
* #return array
*/
public function removeNullValues(array $data)
{
$filtered_data = [];
foreach ($data as $key => $value) {
$filtered_data[$key] = $this->when($value !== null, $value);
}
return $filtered_data;
}
}
Then use it in your resource
class UserResource extends JsonResource
{
use ResourceHelpers;
/**
* Transform the resource into an array.
*
* #param \Illuminate\Http\Request $request
* #return array
*/
public function toArray($request)
{
return $this->removeNullValues([
"id" => $this->id,
"first_name" => $this->first_name,
"last_name" => $this->last_name,
"phone" => $this->phone,
"email" => $this->email,
"balance" => $this->balance,
'address' => $this->address,
'city' => $this->city,
'state' => $this->state,
'zip_code' => $this->zip_code,
'country' => CountryResource::make($this->whenLoaded('country')),
"joined_at" => $this->created_at,
"updated_at" => $this->updated_at,
]);
}
}
In your resource:
$return = parent::toArray($request);
unset($return['email']);
return $return;
Or, you can explicitly define just what you do want to return, instead of relying on toArray:
return [
'id' => $this->id,
'message' => $this->message,
// and so on...
];
This gives you more control over the final API response, especially if you make database/model changes but need the API to remain unchanged.

Categories