Nested collection resolve in Laravel Resource - php

I've got two one-to-many relationships in Laravel, where Cities(id, name, region_id) <- Regions(id, name, country_id) <- Countries(id, name).
I'd need to create a Resource that can return all the Cities in all the Regions in a given Country, like:
"results": [
{
"region": 1,
"name": "Abruzzo",
"cities": {
{"id": 1,
"name": "Aaa"},
{"id": 2,
"name": "Aab"},
},
},
{
"region": 2,
"name": "Basilicata",
"cities": {
{"id": 1,
"name": "Baa"},
{"id": 2,
"name": "Bab"},
},
}
for just one level of relationship, i would create a RegionResource, a RegionsResource and use the Resolve() method inside a, let's say, ResultsResource, like:
'regions' => (new RegionsResource($country))->resolve(),
but I don't seem to be able to do it in a recursive way for both the levels. Could anyone help me? Thanks.

Solved thanks to my colleague. Here's the code:
$cities = collect();
$regions->each(function($region) use(&$cities) {
$cities->push($region->cities);
});

Related

Group search result from multiple indices to single hits in Algolia

I am trying to implement the Algolia search. I am using PHP.
Scenario:
I have three tables (products, resources, and news). I am currently using MultipleQueries (DOCs Link Here) from this documentation.
As a result, I am getting results in the following format as in the documentation.
{
"results": [
{
"hits": [
{
........
},
],
"page": 0,
"nbHits": 1,
"nbPages": 1,
"hitsPerPage": 20,
"processingTimeMS": 1,
"query": "jimmie paint",
"params": "query=jimmie+paint&attributesToRetrieve=firstname,lastname&hitsPerPage=50"
"index": "people"
},
{
"hits": [
{
...........
},
{
...........
}
],
"page": 0,
"nbHits": 1,
"nbPages": 1,
"hitsPerPage": 20,
"processingTimeMS": 1,
"query": "jimmie paint",
"params": "query=jimmie+paint&attributesToRetrieve=firstname,lastname&hitsPerPage=50"
"index": "famous_people"
},
{
..............
}
]
}
This is great, but WHAT I WANT is to group the results of 3 indices into single hits. Below is a sample I am expecting from the API.
{
"results": [
{
"hits": [
{
........
},
{
........
},
{
........
},
{
........
},
{
........
},
{
........
},
{
........
},
],
"page": 0,
"nbHits": 1,
"nbPages": 1,
"hitsPerPage": 20,
"processingTimeMS": 1,
"query": "jimmie paint",
"params": "query=jimmie+paint&attributesToRetrieve=firstname,lastname&hitsPerPage=50"
"index": "indices goes here"
},
]
}
I searched a lot but could not come with suitable solution. Is this even possible using Algolia. Any help would be greatly appreciated.
Thank you in advance.
There's no concept of aggregation across multiple indices baked into Algolia. You'll have to aggregate the hit records yourself via code before displaying.
It's more typical for Algolia users to display results from multiple indices in a federated way using one of the front end libraries. Autocomplete is great at this:
https://www.algolia.com/doc/guides/solutions/ecommerce/search/autocomplete/federated-search/#combining-different-data-sources

Laravel: How do i return data combinely when one table is related to more than one table?

So, in my application I have models: user, category, service(i.e subcategory) and userService.
There exists many to many relationship between user and category, user and service. And one to many between category and service.
// Tables
Users : id, name, email, password
Categories : id, name
Services : id, name, category_id
user_services: id, user_id, category_id, service_id
Relationship is defined in model as:
// User Model
public function services()
{
return $this->belongsToMany('App\Models\V1\Service', 'user_services', 'user_id', 'service_id');
}
public function categories()
{
return $this->belongsToMany('App\Models\V1\Category', 'user_services', 'user_id', 'category_id');
}
// category model
public function services()
{
return $this->hasMany(Service::class);
}
After populating my db with some random data, the user_services table look like this:
// user_services table
id user_id category_id service_id
1 1 1 1
2 1 1 2
3 2 1 1
so far code is good but when i want particular user with services and categories, i am not able to get it as i expected.
The eloquent method that used to fetch user with services and categories:
public function userWithCategoryServices(Request $request)
{
return User::where('id', $request->id)->with(categories.services)->get();
}
The problem with this is:
As there is case one user with one category can have multiple services, so this eloquent method will return same category multiple times. But i want all services belonging to one category grouped in single category key for that particular user.
Another problem, instead of fetching services of that particular user only,but it will fetch all services from db for users particular category.
[
{
"id": 1,
"name": "Ashish Bogati",
"email": "abbaasdaashisdh23#gmail.com",
"categories": [
{
"id": 1,
"name": "IT",
"pivot": {
"user_id": 1,
"category_id": 1
},
"services": [
{
"id": 1,
"name": "Web Developer",
"category_id": 1,
},
{
"id": 2,
"name": "Web Design",
"category_id": 1
},
{
"id": 3,
"name": "Database Design",
"category_id": 1
}
]
},
{
"id": 1,
"name": "IT",
"pivot": {
"user_id": 1,
"category_id": 1
},
"services": {
"id": 1,
"name": "Web Developer",
"category_id": 1
},
{
"id": 2,
"name": "Web Design",
"category_id": 1
},
{
"id": 3,
"name": "Database Design",
"category_id": 1
}
]
}
]
}
]
As in this response, category name IT is repeated twice for user and all services are fetched for that category instead of for particular user only.
Response i wanted:
[
{
"id": 1,
"name": "Ashish Bogati",
"email": "abbaasdaashisdh23#gmail.com",
"categories": [
{
"id": 1,
"name": "IT",
"pivot": {
"user_id": 1,
"category_id": 1
},
"services": [
{
"id": 1,
"name": "Web Developer",
"category_id": 1,
},
{
"id": 2,
"name": "Web Design",
"category_id": 1
},
]
}
]
the database design is a little strange.we meet duplicate category_id in user_services.but that's is not the problem
//in service model
public function category()
{
return $this->hasOne(Category::class);
}
public function userWithCategoryServices(Request $request)
{
$services = User::where('id', $request->id)->with("services.category")->get();
$servicesGrouped = $services['services']->groupBy('category_id');
}
the services will be right.there will be no more unwanted data.
this is so far we can do by Laravel eloquent.
because the relation map is user->services->category.not user->categories->services
use php to build the structure of the wanted response.
But as Restful principle.you should make api pure.which means one api give only one type of entity.client will be more clear with the rest api and deal with complex structure themself.
public function userWithCategoryServices(Request $request)
{
$services = User::where('id', $request->id)->with("services.category")->get()->toArray();
$categories = []
for($services as $service){
if(!isset($categories[$service['category_id']])){
$categories[$service['category_id']] = $service['category'];
$categories[$service['category_id']]['services'] = [];
}
unset($service['category']);
$categories[$service['category_id']]['services'][] = $service;
}
$categories = array_values($categories);
unset($services['services']);
$services['categories'] = $categories;
return $services;
}

Laravel - How to pluck field from nested relations

I'm trying to pluck field from nested relations.
My class structure is:
User hasMany GroupUser
GroupUser belongsTo Group
Group belongsToMany Promotion
So this way I can get fine get Promotions from an User
$user->groupUsers()->with('group')->with('group.promotions')->get();
{
"id": 4,
....
"promotions": [
{
"id": 3,
"group_id": 11,
"user_id": 4,
...
"group": {
"id": 11,
...
"promotions": [
{
"id": 8,
"title": "Lavagem Mensal 1 unid.",
"const": "LAVAGEM_MENSAL1_UNID",
"pivot": {
"group_id": 11,
"promotion_id": 8
}
}
]
}
}
]
}
So what I need is to get a pluck list of Promotions.const.
Tried this way but it returns [null]
$user->groupUsers()->with('group')->with('group.promotions')->get()->pluck('group.promotions.const');
UPDATE: Now this way I close to achieve it.
$user->groupUsers()->with('group')->with('group.promotions')->get()->pluck('group.promotions.*.const');
"promotions": [
[
"BRF_LAVAGEM_MENSAL_1_UNID"
]
]
Just need to flat this array.
Try this:
$user->groupUsers()
->with('group.promotions')
->get()
->pluck('group.promotions.*.const')
->collapse();

Whats the best approach to create filter-based search in Laravel or raw SQL?

I'm working on a query to find offers based in different filters and need some guidance on how to do that.
My model structure is basically: offer -> place -> categories -> filters -> filterOptions
offer (belongsTo) <-> (hasMany) place
place (belongsToMany) <-> (belongsToMany) category
place (belongsToMany) <-> (belongsToMany) filter
filter (belongsToMany) <-> (belongsToMany) category
filter (hasMany) <-> (belongsToMany) filterOption
Search params comes in request JSON just like this below, which says "get offers from places with category 1 and has those filters + filterOptions"
{
"category_id":1,
"filters":[
{
"filter_id":1,
"options":[
{"option_id":2},
{"option_id":3}
]
},
{
"filter_id":2,
"options":[
{"option_id":4},
{"option_id":6}
]
}
]
}
So far I'm just getting the entire list without filtering.
$q = Offer::with([
'place',
'place.categories',
'place.categories.filters',
'place.categories.filters.filterOptions',
])
This is returning the JSON objects like this:
[
{
"id": 4,
"place_id": 2,
"place": {
"id": 2,
"categories": [
{
"id": 3,
"name": "Mechanics",
"pivot": {
"place_id": 2,
"category_id": 3
},
"filters": [
{
"id": 5,
"name": "Services",
"pivot": {
"category_id": 3,
"filter_id": 5,
},
"filter_options": [
{
"id": 8,
"name": "Tires",
"filter_id": 5
},
{
"id": 9,
"name": "Painting",
"filter_id": 5
}
]
},
{
"id": 6,
"name": "Amenities",
"pivot": {
"category_id": 3,
"filter_id": 6,
},
"filter_options": [
{
"id": 11,
"name": "Wifi",
"filter_id": 6
},
{
"id": 12,
"name": "Food",
"filter_id": 6
}
]
}
]
}
]
}
}
]
I have no idea on what is the starting point to perform this filtering.
Should I use eloquent or raw query? single or multiple queries? joins or grouping?
Solved this way:
$params = json_decode($request->getContent());
$q = Offer::with([
'place:id,title',
'place.categories:id,name',
'place.categories.filters:id,name',
'place.categories.filters.filterOptions:id,name,filter_id'
])
->whereHas('place.categories', function($q) use ($params){
$q->where('id', $params->category_id);
})
->whereHas('place.categories.filters', function($q) use ($params){
$filters = array_flatten(array_pluck($params->filters, 'filter_id'));
$q->whereIn('id', $filters);
})
->whereHas('place.categories.filters.filterOptions', function($q) use ($params){
$filterOptions = array_flatten(array_pluck($params->filters, 'options.*.option_id'));
$q->whereIn('id', $filterOptions);
})
->select('id','percent','place_id')
->get();

Laravel loop based on inner values

I have a competition model (the competition is essentially a large form to fill in), that hasMany groups (groups of fields), which hasMany fields. When a user fills in a form, I create one entry record which hasMany field submissions (called elements). So, one entry might look like the following:
id form_id
-------------
2 5
The the field submissions (elements) table may look like the following:
id entry_id field_id value
----------------------------------
1 2 2 Dave
2 2 3 30
So if I run:
$entry = Entry::with('elements.field.group')->find($id);
I get something like this:
{
"id": 16,
"competition_id": 1,
"elements": [
{
"id": 8,
"field_id": 1,
"entry_id": 16,
"content": "F",
"field": {
"id": 1,
"name": "gender",
"label": "Gender",
"type": "radio",
"group_id": 1,
"group": {
"id": 1,
"order": 1,
"competition_id": 1
}
}
},
{
"id": 9,
"field_id": 2,
"entry_id": 16,
"content": "fds",
"field": {
"id": 2,
"name": "name",
"label": "Your name",
"type": "text",
"group_id": 1,
"group": {
"id": 1,
"order": 1,
"competition_id": 1
}
}
}
]
}
To display these, I'd like to ideally do a #foreach($groups as $group), but the groups only exist in element->field->group. How can I go about reorganising the array to allow for this? Is there a way to rearrange the array from the inside out? Or is there a way of sorting the data as I grab it from Laravel?
Maybe this works:
$groups = Group::with('fields.element.entry')->whereHas('field.element.entry', function($q) use ($id){
$q->where('entries.id', $id);
})->get();

Categories