How to covert Laravel's collection to array object collection? - laravel

I have small laravel collection as below.
[
{
id: 1,
data1: 11,
data2: 12,
data3: 13,
created_at: null,
updated_at: null
},
{
id: 2,
data1: 14,
data2: 15,
data3: 16,
created_at: null,
updated_at: null
}
]
But I would like to convert to array collection like below.
{
data: [
[
11,
12,
13
],
[
14,
15,
16
]
]
}
Appreciated for advice and so sorry for my English. Thank you very much.

Use toArray() which converts this object into an array.
$data->toArray();
Now the collection converted into an array and looks like:-
[
[
id: 1,
data1: 11,
data2: 12,
data3: 13,
created_at: null,
updated_at: null
],
[
id: 2,
data1: 14,
data2: 15,
data3: 16,
created_at: null,
updated_at: null
]
]
But as per your requirements, you don't want associative index for the array, So use
$data = array_values($data);
Now your keys has been removed and final data is:-
[
[
11,
12,
13
],
[
14,
15,
16
]
]

Related

using pluck on nested results in Laravel

I have a query that returns the following results.
Illuminate\Database\Eloquent\Collection {#5145
all: [
App\Models\Result {#5207
id: 198,
result_id: 30,
result_type: "App\Models\Touchpoint",
audit_id: 1,
weight: 7,
pics: 0,
recs: 0,
rating: 4,
comments: "none",
complete: 1,
created_at: "2022-06-03 03:42:24",updated_at: "2022-06-03 03:42:24",
result: App\Models\Touchpoint {#5210
id: 30,
name: "Lineman",
description: "The location food offer was available on Lineman",
sort_order: 25,
req_pics: 0,
req_recs: 0,
sector_id: 1,
created_at: null,
updated_at: "2022-04-02 14:02:34",
},
},
App\Models\Result {#5119
id: 199,
result_id: 29,
result_type: "App\Models\Touchpoint",
audit_id: 1,
weight: 7,
pics: 0,
recs: 0,
rating: 4,
comments: "none",
complete: 1,
created_at: "2022-06-03 03:43:38",
updated_at: "2022-06-03 03:43:38",
result: App\Models\Touchpoint {#5206
id: 29,
name: "Grab",
description: "The location food offer was available on Grab",
sort_order: 24,
req_pics: 0,
req_recs: 0,
sector_id: 1,
created_at: null,
updated_at: "2022-04-02 14:02:26",
},
},
],
}
This is the query I'm using to get that collection and I want the result to just contain sort_order, name, description, rating and weight from these results and place them in an array. I'm assuming I need to use pluck to get the correct fields but when I try to pluck 'result.name', etc. I get told result doesn't exist.
$result = Result::query()->where('audit_id', 1)->where('result_type', 'App\Models\Touchpoint')->whereIn('result_id', $tps)->with('result')->get();
This needs to be in a query without manipulating the collection as I need to feed it into Maatwebsite\Excel\Concerns\WithMultipleSheets, which requires a query not the query results.
You can use eager load constraints
$result = Result::query()
->where('audit_id', 1)
->where('result_type', 'App\Models\Touchpoint')
->whereIn('result_id', $tps)
->with('result:id,sort_order,name,description')
->select('id', 'result_id', 'rating', 'weight')
->get();
If you want to remove the nesting and flatten the result set you can map() over the collection
$result->map(function($item) {
$item['sort_order'] = $item->result->sort_order;
$item['name'] = $item->result->name;
$item['description'] = $item->result->description;
//remove the nested relation
$item->unsetRelation('result');
})
->toArray();
/**
* Will return something like
[
[
'id' => 198,
'result_id' => 30,
'rating' => 4,
'weight' => 7,
'sort_order' => 25,
'name' => 'Lineman',
'description' => 'The location food offer was available on Lineman'
],
[
'id' => 199,
'result_id' => 29,
'rating' => 4,
'weight' => 7,
'sort_order' => 24,
'name' => 'Grab',
'description' => 'The location food offer was available on Grab'
]
]
*/
Laravel Docs - Eloquent Relationships - Constraining Eager Loads

Inconsistant Results from Searchkick / Elasticsearch Aggregations

First off I will work on cleaning up the output from IRB (this was from production).
I was using some search kick aggregations in my Rails app. tested fine in development now in production I am seeing inconsistent results. I have data in fields that should clearly show up in my aggregations.
Here is my plain old Active Record Data - my BudgetItem model has a total:
irb(main):008:0> BudgetItem.where(budget_id: 3).order(:cbs_item_id).select(:id, :cbs_item_id, :total)
BudgetItem Load (1.4ms) SELECT "budget_items"."id", "budget_items"."cbs_item_id", "budget_items"."total" FROM "budget_items" WHERE "budget_items"."company_id" = 26 AND "budget_items"."budget_id" = $1 ORDER BY "budget_items"."cbs_item_id" ASC LIMIT $2 [["budget_id", 3], ["LIMIT", 11]]
=> #<ActiveRecord::Relation [#<BudgetItem id: 28, company_id: 26, cbs_item_id: 3, total: 0.1e5>, #<BudgetItem id: 29, company_id: 26, cbs_item_id: 12, total: 0.8e5>, #<BudgetItem id: 34, company_id: 26, cbs_item_id: 15, total: 0.1e5>, #<BudgetItem id: 41, company_id: 26, cbs_item_id: 16, total: 0.141e6>, #<BudgetItem id: 35, company_id: 26, cbs_item_id: 18, total: 0.1e5>, #<BudgetItem id: 33, company_id: 26, cbs_item_id: 18, total: 0.12e5>, #<BudgetItem id: 27, company_id: 26, cbs_item_id: 20, total: 0.2e5>, #<BudgetItem id: 6, company_id: 26, cbs_item_id: 23, total: 0.184e6>, #<BudgetItem id: 5, company_id: 26, cbs_item_id: 23, total: 0.2288e6>, #<BudgetItem id: 30, company_id: 26, cbs_item_id: 24, total: 0.45e5>, ...]>
The count on this is 41
the same search via Searchkick:
BudgetItem.search("*", where: {budget_id: 3}).count = 41
and even this:
irb(main):025:0> BudgetItem.search("*", where: {budget_id: 3, cbs_item_id: 16}).first.total
= 26 AND "budget_items"."id" = $1 [["id", 41]]
=> 0.141e6
Note cbs_item_id: 16, total: 0.141e6 (141000) - the value is clearly in the model.
Now I try and run aggregates on this:
irb(main):019:0> BudgetItem.search("*", body_options: { aggs: { cbs: { terms: { field: "cbs_item_id" }, aggs: { "total": { "sum": { "field": "total" } } } } } }, where: {budget_id: 3}).aggs
BudgetItem Search (5.4ms) pacific-canbriam-20191213_budget_items_production/_search {"query":{"bool":{"must":{"match_all":{}},"filter":[{"term":{"budget_id":{"value":3}}}]}},"timeout":"11s","_source":false,"size":10000,"aggs":{"cbs":{"terms":{"field":"cbs_item_id"},"aggs":{"total":{"sum":{"field":"total"}}}}}}
=> {"cbs"=>{"doc_count_error_upper_bound"=>0, "sum_other_doc_count"=>13, "buckets"=>[{"key"=>24, "doc_count"=>4, "total"=>{"value"=>90000.0}}, {"key"=>25, "doc_count"=>4, "total"=>{"value"=>114000.0}}, {"key"=>39, "doc_count"=>4, "total"=>{"value"=>107325.0}}, {"key"=>43, "doc_count"=>4, "total"=>{"value"=>209820.0}}, {"key"=>18, "doc_count"=>2, "total"=>{"value"=>22000.0}}, {"key"=>23, "doc_count"=>2, "total"=>{"value"=>412800.0}}, {"key"=>38, "doc_count"=>2, "total"=>{"value"=>13500.0}}, {"key"=>49, "doc_count"=>2, "total"=>{"value"=>161000.0}}, {"key"=>57, "doc_count"=>2, "total"=>{"value"=>20300.0}}, {"key"=>58, "doc_count"=>2, "total"=>{"value"=>32200.0}}]}}
The data is completely inconsistent and aggs missing. Note key 16 is missing. The crazy thing is I have another aggregation on another column and that one works absolutely fine. Am i missing something here? I have already tried settings: {number_of_shards: 1}.
To be more frustrating this works:
aggs: {
"grand_total": { "sum": { "field": "total" } },
}
The regular agg for total works in the same result.
Try setting size in the aggregation. Based on sum_other_doc_count in the response, 13 documents aren't being returned.

How to return only the child models of a one to many relationship - Laravel

I was hoping I could get some assistance here.
Here is my model structure:
Property 1
--Image 1
--Image 2
Property 2
--Image 3
--Image 4
What I'm trying to do is retrieve all Image models.
This is what I have tried:
$properties = Auth::user()
->landlord_profile_auto
->properties()
->with('images')
->get();
dd($properties->images);
Property [images] does not exist on this collection instance.
Thanks a lot
EDIT:
Here is the dump of a property:
>>> Property::whereHas('images')->with('images')->first()
=> App\Models\Property {#3064
id: 3,
created_at: "2019-01-23 17:31:34",
updated_at: "2019-01-23 20:22:45",
address_line_1: "ABC",
address_line_2: "ABC",
unit: "calculateStorageUsage",
city: "ABC",
postal_code: "calculateStorageUsage",
url_slug: null,
is_draft: 0,
is_refurb: 0,
purchased_date: "2019-01-23",
bedrooms: 3,
max_tenants: null,
rent_amount: "589595.00",
currency_id: 252,
country_id: 491,
rent_frequency_id: 7,
property_type_id: null,
featured_image_id: 4,
landlord_profile_id: 6,
images: Illuminate\Database\Eloquent\Collection {#3059
all: [
App\Models\Image {#3078
id: 4,
created_at: "2019-01-23 20:22:42",
updated_at: "2019-01-23 20:22:42",
name: null,
caption: null,
path: "property_images/Ba1XYIB394xLsH4365391beAZ.jpg",
size_kb: 849.85,
imageable_type: "App\Models\Property",
imageable_id: 3,
},
App\Models\Image {#3077
id: 5,
created_at: "2019-01-23 20:22:45",
updated_at: "2019-01-23 20:22:45",
name: null,
caption: null,
path: "property_images/An3bgmKJzMcPuZyYizp9Lm6dj.jpg",
size_kb: 849.85,
imageable_type: "App\Models\Property",
imageable_id: 3,
},
],
},
}
Properties has a one to many relationship to images. Images is a polymorphic table.
Try this:
#foreach($properties->images as $image)
{
// here you can acess each image
}
#endforeach

Merging Collection if values are equals for a specific key with Laravel

I have 2 different collections which look like this
Collection 1 :
{typesession_id: 3, formation_id: 7, codeformation: "FR8", id: 3, nomformation: "Sécurité électrique", …}
{typesession_id: 3, formation_id: 8, codeformation: "FR8", id: 4, nomformation: "Sécurité incendie", …}
Collection 2 :
{datesession: "2018-03-15", debutsession: "08:30:00", finsession: "12:00:00", typesession_id: 1, title: "Gestes d'urgence - Soignant", …}
{datesession: "2018-03-16", debutsession: "08:30:00", finsession: "12:00:00", typesession_id: 3, title: "eLearning", …}
{datesession: "2018-03-17", debutsession: "08:30:00", finsession: "12:00:00", typesession_id: 3, title: "eLearning", …}
Those 2 collection have a common key ...
I'm trying to recover the elements of the Collection 2 which have the same value for the "typesession_id" key ...
For example if my collection 1 have elements with typesession_id equal to 1 and 2, i want to have items of the collection 2 with values 1 and 2.
My newCollection should be :
{typesession_id: 3, formation_id: 7, codeformation: "FR8", id: 3, nomformation: "Sécurité électrique", datesession: "2018-03-16", debutsession: "08:30:00", finsession: "12:00:00", title: "eLearning", …}
{typesession_id: 3, formation_id: 7, codeformation: "FR8", id: 4, nomformation: "Sécurité électrique", datesession: "2018-03-17", debutsession: "08:30:00", finsession: "12:00:00", title: "eLearning", …}
{typesession_id: 3, formation_id: 8, codeformation: "FR8", id: 3, nomformation: "Sécurité incendie", datesession: "2018-03-16", debutsession: "08:30:00", finsession: "12:00:00", title: "eLearning", …}
{typesession_id: 3, formation_id: 8, codeformation: "FR8", id: 4, nomformation: "Sécurité incendie", datesession: "2018-03-17", debutsession: "08:30:00", finsession: "12:00:00", title: "eLearning", …}
In other terms : my first collection allows me to know which are the trainings an user can do ... and my second collection allows me to have information concerning sessions available for a specific type of session (typesession_id). I want to have a full list of combination based on typesession_id ...
I tried to figure it out by myself on Laravel guide but i didn't really find my solution...
I haven't tested but code likes below would probably work:
$collection1 = $collection1->groupBy('typessesion_id');
$result = $collection2->map(function($item, $key) {
$typesession_id = $item->typesession_id;
$item_clone = $item;
$data = $collection1[$typesession_id]->map(function($it, $k) use ($item_clone) {
if ($typesession_id == $it->typesession_id) {
return $item_clone->merge($it);
}
return $it;
});
return $data->merge($item);
});
dd($result);

Keying an eager-loaded relationship

I have a Business model and an Hour model. The Business model overrides the protected $with method to eager load it's hours() hasMany relationship.
When I ::first() a given business I receive something like this:
App\Business {#770
id: 5,
user_id: 5,
name: "Wehner-Hudson",
slug: "wehner-hudson",
lat: "55.33593500",
lng: "112.34818600",
created_at: "2018-01-04 13:00:48",
updated_at: "2018-01-04 13:00:48",
hours: Illuminate\Database\Eloquent\Collection {#753
all: [
App\Hour {#802
id: 13,
business_id: 5,
weekday_id: 3,
open: 1,
split_shift: 1,
},
App\Hour {#803
id: 14,
business_id: 5,
weekday_id: 5,
open: 0,
split_shift: 1,
},
App\Hour {#804
id: 15,
business_id: 5,
weekday_id: 2,
open: 1,
split_shift: 0,
},
],
},
},
],
}
I would like to key the hours: Illuminate\Database\Eloquent\Collection {#753 by weekday_id to facilitate processing on the client side. Something like this:
Illuminate\Database\Eloquent\Collection {#763
all: [
1 => App\Hour {#796
id: 1,
business_id: 1,
weekday_id: 1,
open: 1,
split_shift: 1,
},
5 => App\Hour {#767
id: 2,
business_id: 1,
weekday_id: 5,
open: 0,
split_shift: 0,
},
2 => App\Hour {#765
id: 3,
business_id: 1,
weekday_id: 2,
open: 1,
split_shift: 1,
},
],
}
I tried to use keyBy on the relationship in the Business model:
public function hours()
{
return $this->hasMany(Hour::class)->keyBy('weekday_id');
}
But it is not working, as I believe that at that point the returned object is a builder, not a collection.
Try to define an accessor, like this:
public function getHoursByWeekdayAttribute()
{
return $this->hours->keyBy('weekday_id');
}
What about using groupby in your controller.
Business::with(['hours' => function($query){ $query->groupBy('weekend_id'); }])->get();

Resources