PHP Collection flatten object only if it contains a key - php

I want to flatten some items in an array if they contain a key of items so for example the following object:
{
"key1": {
"order": 1,
"name": "Test"
},
"group1": {
"order": 1,
"name": "Test",
"items": {
"key2": {
"order": 1,
"name": "Test"
},
"key3": {
"order": 1,
"name": "Test"
}
}
}
}
Should be flattened to be formatted like follows, so basically removing the items within the group to the root level:
{
"key1": {
"order": 1,
"name": "Test"
},
"key2": {
"order": 1,
"name": "Test"
},
"key3": {
"order": 1,
"name": "Test"
}
}
I have put this object into a Illuminate\Database\Eloquent\Collection so I can use flatMap. flatten doesn't work, as I don't want to completely flatten it.

Assuming you are using Laravel, did you take a resource and collection in consideration? You could also try the Laravel collapse() method which can be found here: collapse method on collections
$collection = collect([
[1, 2, 3],
[4, 5, 6],
[7, 8, 9],
]);
$collapsed = $collection->collapse();
$collapsed->all();
// [1, 2, 3, 4, 5, 6, 7, 8, 9]

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'

multidimensional array merge into one array if key is same without using nested loop

[
{
"status": true,
"data": [
{
"id": 2,
"name": "Algeria",
"country_code": "DZ",
"regions": [
{
"id": 2,
"country_id": 2,
"region_code": "RG01",
"depots": [
{
"id": 5,
"region_id": 2,
"depot_code": "DP04",
"depot_name": "North Depot",
"area": [
{
"id": 1,
"depot_id": 5,
"area_code": "AR1",
"area_name": "Area-1"
},
{
"id": 2,
"depot_id": 5,
"area_code": "AR2",
"area_name": "Area-2"
}
]
},
{
"id": 6,
"region_id": 2,
"depot_code": "DP06",
"depot_name": "east Depot",
"area": [
{
"id": 3,
"depot_id": 6,
"area_code": "AR3",
"area_name": "Area-3"
}
]
}
]
},
{
"id": 3,
"country_id": 2,
"region_code": "RG02",
"depots": []
}
]
}
]
}
]
i want to merge area into single array like
"area": [
{
"id": 1,
"depot_id": 5,
"area_code": "AR1",
"area_name": "Area-1"
},
{
"id": 2,
"depot_id": 5,
"area_code": "AR2",
"area_name": "Area-2"
},
{
"id": 3,
"depot_id": 6,
"area_code": "AR3",
"area_name": "Area-3"
}
]
i don't want to use multiple foreach.
thanks in advance.
You can use the following code to recursively iterate of all your structure and extract only data you need (by key). I assume $data has your original data structure and within $result you will get array of area:
$it = new RecursiveIteratorIterator(new RecursiveArrayIterator($data), RecursiveIteratorIterator::SELF_FIRST);
$result = [];
foreach ($it as $k => $v) {
if ($k === 'area') {
$result = array_merge($result, $v);
}
}
I would not overcomplicate things, loop through you data structure. Add all areas to a collection. Prepare your data structure and i added a check to secure there is no duplicates with unique().
$data = 'your-data';
$areas = collect();
foreach($data->data as $country) {
foreach($country->regions as $region) {
foreach ($region->depots as $depot) {
$areas->concat($depot->area);
}
}
}
$result = ['area' => $areas->unique('id')->all()];

Laravel make data unique but remove first duplicate

I got data from db:
$message = Message::where('user_id', $user->id)->get();
Result:
[
{
"id": 1,
"post_id": 3,
"agent_id": 1,
"user_id": 1,
"message": "hello",
"status": 0
},
{
"id": 2,
"post_id": 3,
"agent_id": 1,
"user_id": 1,
"sender_id": 1,
"message": "how are you?",
"status": 0
},
{
"id": 3,
"post_id": 3,
"agent_id": 1,
"user_id": 1,
"message": "Ill call you",
"status": 0
},
{
"id": 4,
"post_id": 5,
"agent_id": 1,
"user_id": 2,
"message": "hi hello",
"status": 0
}
]
Well, I able to make it unique and remove duplicate by user_id with:
$message->unique('user_id')
It return right result, but what I want is, remove first item not last, current result:
{
"0": {
"id": 1,
"post_id": 3,
"agent_id": 1,
"user_id": 1,
"message": "hello",
"status": 0
},
"3": {
"id": 4,
"post_id": 5,
"agent_id": 1,
"user_id": 2,
"message": "hi hello",
"status": 0
}
}
As you see, it keep first item and then removed next duplicate, in this case I want to keep id 3 and 4 also it made my object to array, I don't want this, how can I solve this?
But I want this result:
[
{
"id": 3,
"post_id": 3,
"agent_id": 1,
"user_id": 1,
"message": "Ill call you",
"status": 0
},
{
"id": 4,
"post_id": 5,
"agent_id": 1,
"user_id": 2,
"message": "hi hello",
"status": 0
}
]
To get the newest messages try sorting on id in a descending order
$messages = Message::where('user_id', $user->id)->orderBy('id', 'DESC')->get();
and you are receiving a Collection you call toArray on it to get back an array:
return $messages->unique('user_id')->toArray();
Use sorting before unique.
$message = Message::where('user_id', $user->id)->orderBy('id','desc')->get();
$message->unique('user_id');

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;
});
}

How can I add muti-dimensional array to laravel database

I want to store product option on my database and loop through it when I retrieve the data. The data structure takes a form
[
{
"color": "black",
"extralCost": 100,
"size": {
"xs": 1,
"sm": 4,
"L": 2,
"XL":6
},
"stock":13
}, {
"color": "white",
"extralCost": 0,
"size": {
"xs": 1,
"sm": 4,
"L": 2
},
"stock":7
},
...
]
I want to be able to store this in the database and retrieve it at the view.
Add this to your Model.
protected $casts = [
'column_name' => 'array',
];
To get an array:
$request->except('_token', '_method')

Categories