Lumen/Laravel Eloquent hasManyThrough nested results - php

I`m trying to achieve a nested result using Laravel Eloquent.
My logic:
Cities, Neighbourhoods, Blocks
A city has many neighbourhoods
A neighbourhood has many blocks
The result that I want to acomplish:
City
Neighbourhoods
Blocks
The data shoud be:
[
{
id: 1,
name: "New York",
neighbourhoods: [
{
id: 1,
name: "Neighbourhood 1",
city_id: 1,
blocks: [
{
id: 1,
name: "Neighbourhood 1 Block 1",
neighbourhood_id: 1
},
{
id: 2,
name: "Neighbourhood 1 Block 2",
neighbourhood_id: 1
}
]
},
{
id: 2,
name: "Neighbourhood 2",
city_id: 1,
blocks: [
{
id: 3,
name: "Neighbourhood 2 Block 1",
neighbourhood_id: 2
},
{
id: 4,
name: "Neighbourhood 2 Block 2",
neighbourhood_id: 2
}
]
}
]
}
]
Models:
City.php
public function neighbourhood()
{
return $this->hasMany('App\Neighbourhood');
}
public function block()
{
return $this->hasManyThrough('App\Block', 'App\Neighbourhood', 'city_id', 'neighbourhood_id', 'id');
}
Neighbourhood.php
public function city()
{
return $this->belongsTo('App\City');
}
public function block()
{
return $this->hasMany('App\Block');
}
Block.php
public function neighbourhood()
{
return $this->belongsTo('App\Neighbourhood');
}
The actual result gives me this:
[
{
"id": 1,
"name": "Ney York",
"neighbourhoods": [
{
"id": 1,
"city_id": 1,
"name": "Heighbourhood 1"
},
{
"id": 2,
"city_id": 1,
"name": "Heighbourhood 2"
},
{
"id": 4,
"city_id": 1,
"name": "Păcurari"
}
],
"blocks": [
{
"id": 1,
"name": "Heighbourhood 1 Block 1",
"neighbourhood_id": 1,
"city_id": 1
},
{
"id": 2,
"name": "Heighbourhood 1 Block 2",
"neighbourhood_id": 1,
"city_id": 1
}
]
}
]
I want to get the results nested. Is there a way to do this? I`m missing something?
Of course that it can me done with PHP foreach loop but this will be manual job. I wonder if I can get the results this way directly from the query.
Thank you.
SOLUTION:
$result = City::with('neighbourhoods.blocks')->get();

This should do:
$result = City::with('neighbourhoods.blocks')->get();
You can do nested relationship queries with dot notation.

Related

filter one to many relationship base on parent column in laravel

This link Laravel filter one to many relationship has a solution related to my question but I don't know why the solutions are not working for me.
I am currently selecting all the offtaker sites and the apartments the site have but I want to filter the apartment to only apartment that the apartment id is equal to the offtakers apartment_id
Below are my model setup
offtaker model
public function sites(){
return $this->belongsTo(Sites::class,'site_id','id')->select('id','name');
}
site model
public function apartment(){
return $this->hasMany(Apartment::class,'site_id','id');
}
offtaker controller
return Offtakers::select(['id','site_id','offtaker_id','company_id','apartment_id'])
->with('sites',
)->with('sites.apartment',function($query){
$query->where(function($query){
$query->whereRaw("id = (select apartment_id from offtakers where apartment_id=apartments.id)");
});
})->get();
Current out put
{
"id": 2,
"site_id": "1",
"offtaker_id": 6,
"apartment_id": "8",
"sites": {
"id": 1,
"name": "first site",
"apartment": [
{
"id": 8,
"company_id": 1,
"site_id": 1,
"project_id": null,
"apartment_name": "apartment five",
"apartment_description": "test descriptions",
"amount": 8000
},
{
"id": 55,
"company_id": 1,
"site_id": 1,
"project_id": null,
"apartment_name": "apartment eleven",
"apartment_description": "test descriptions",
"amount": 550000
}
]
}
},
Expected out put
{
"id": 2,
"site_id": "1",
"offtaker_id": 6,
"apartment_id": "8",
"sites": {
"id": 1,
"name": "first site",
"apartment": [
{
"id": 8,
"company_id": 1,
"site_id": 1,
"project_id": null,
"apartment_name": "apartment five",
"apartment_description": "test descriptions",
"amount": 8000
},
]
}
},
For you, I suggest you add this package https://github.com/staudenmeir/eloquent-has-many-deep.
This will solve the 2 deep relationships Offtaker - Site - Apartment
Add new relationship in Offtaker model
public function apartments()
{
return $this->hasManyThrough(Apartment::class, Site::class);
}
And to use it:
return Offtaker::select(['id','site_id','offtaker_id','company_id','apartment_id'])
->with([
'sites' => function ($q) {
$q->where('id', 'offtakers.site_id');
},
'apartments' => function ($q) {
$q
->where('site_id', 'offtakers.site_id')
->where('apartment_id', 'offtakers.apartment_id');
}
])
->get();
However the result might a bit different that want you want
{
"id": 2,
"site_id": 1,
"offtaker_id": 6,
"apartment_id": 8,
"sites": [
{
"id": 1,
"name": "first site",
}
],
"apartments": [
{
"id": 8,
"company_id": 1,
"site_id": 1,
"project_id": null,
"apartment_name": "apartment five",
"apartment_description": "test descriptions",
"amount": 8000
}
]
},
....
I hope this can help you out.

How To Get Specifie Product in Laravel Array

im trying to get those products that have css and html technology.note that (html and css) are not the only parameters that can be used.sometimes could be more, for example ['java','html,'python',...] and so on
in this result i have 2 product but it should be 1,the one with (id:1) becuz only this product have both (html and css)technology
[
{
"id": 1,
"title": "First Product",
"prte": [
{
"product_id": 1,
"technology_id": 1,
"technology": {
"id": 1,
"title_en": "HTML"
}
},
{
"product_id": 1,
"technology_id": 2,
"technology": {
"id": 2,
"title_en": "CSS"
}
}
]
},
{
"id": 2,
"title": "Second Product",
"prte": [
{
"product_id": 2,
"technology_id": 1,
"technology": {
"id": 1,
"title_en": "HTML"
}
}
]
}
]
and this one is the Controller
$products = Product::with(['prte' => function ($query){
$query->with(['technology' => function ($query){
$query->whereIn('title_en',['HTML','CSS']);
}]);
}])->get();
return view('find',compact('products'));
hope u guys could help me
Try where instead of whereIn.
$products = Product::with(['prte' => function ($query){
$query->with(['technology' => function ($query){
$query->where('title_en','HTML')->where('title_en','CSS');
}]);
}])->get();
return view('find',compact('products'));

laravel 5.6 - get data of multiple layers of parent-child with belongs to many

Imagine I have a site with a lots of catalogs which have sections and with each section, i have items.
catalogs is belongstomany with sections.
sections is belongstomany with items.
What I want for final result is to have a nest data with:
{
"catalog_id": 1,
"catalog_name": "catalog 01",
"sections": [
{
"section_id": 1,
"name": "section 01",
"items": [
{
"item_id": 1,
"item_content": {
"name": "some content1"
}
},
{
"item_id": 2,
"item_content": {
"name": "some content2"
}
},
{
"item_id": 3,
"item_content": {
"name": "some content3"
}
}
]
},
{
"section_id": 2,
"name": "section 02",
"items": [
{
"item_id": 2,
"item_content": {
"name": "some content2"
}
},
{
"item_id": 3,
"item_content": {
"name": "some content3"
}
},
{
"item_id": 4,
"item_content": {
"name": "some content4"
}
}
]
}
]
}
How can I achieve that?
Technically I can use each-loop to get whatever I want, but I hope that is a better solution in Eloquent.
Try this
Catalogs::with('sections.items')->get();
I would add to #J.Doe answer that you can specify what you want to do. for example
Catalogs::with([
'sections' => function($query){
return $query->select('column')
->where('someCondition', 1)
->orderBy('id', 'asc');
'items' => //some other stuff
];
you can also use method with() for your relation. lets say sections related to someTable. if you have relation method someTable in your section model, then:
Catalogs::with([
'sections' => function($query){
return $query->select('column')
->with('someTable') // here's your relation
->where('someCondition', 1)
->orderBy('id', 'asc');
'items' => //some other stuff
];

How to select specific fields from nested relations in Eloquent

I have the following query in Eloquent:
public function firstSubsectionIdsOnly()
{
return $this->model->with(['sections' => function ($q) {
$q->with(['subsections' => function ($q2) {
$q2->first();
}
])->first();
}])->whereHas('sections.subsections')->first();
}
This returns something like this:
{
"id": 1,
"name": "Training exercise",
"entry": "<p>sss</p>",
"created_at": "2018-04-20 09:38:36",
"updated_at": "2018-04-20 10:08:27",
"sections": [
{
"id": 1,
"name": "Section 1 Training",
"parent": null,
"position": 1,
"created_at": "2018-05-04 09:37:23",
"updated_at": "2018-05-04 09:37:23",
"pivot": {
"training_exercise_id": 1,
"section_id": 1
},
"subsections": [
{
"id": 2,
"name": "Subsection 1 training",
"parent": 1,
"created_at": "2018-05-04 09:54:09",
"updated_at": "2018-05-04 09:54:09"
}
]
}
]
}
I would like for it to only select the id fields for each relation so it should return something like this:
{
"id": 1,
"name": "Training exercise",
"entry": "<p>sss</p>",
"created_at": "2018-04-20 09:38:36",
"updated_at": "2018-04-20 10:08:27",
"sections": [
{
"id": 1,
"subsections": [
{
"id": 2,
}
]
}
]
}
I have tried adding $q->select('id'); to the subsections nested closure but that returns an empty subsections array.
Any idea on how I can achieve this? I am using Laravel 5.6
As others have stated before, you need to include the foreign key in the values you select. Let's say we have two models. Parent and Child. This is the bare minimum you have to select.
App\Parent::select('id')->with(['children' => function ($query) {
$query->select('id', 'parent_id');
}])->get();
It nets you something like
Illuminate\Database\Eloquent\Collection {
all: [
App\Parent {
id: 1,
children: Illuminate\Database\Eloquent\Collection {
all: [
App\Child {
id: 109,
parent_id: 1,
},
App\Child {
id: 153,
parent_id: 1,
},
],
},
},
],
}
If you're trying to go for the inverse, you still need the foreign key, like so
App\Child::select('id', 'parent_id')->with('parent' => function ($query) {
$query->select('id');
}])->get();
This nets you
Illuminate\Database\Eloquent\Collection {
all: [
App\Child {
id: 1,
parent_id: 58,
parent: App\Parent {
id: 58,
},
},
],
}
I'm using select() instead of get() for legibility on the queries but you must use select() for the subqueries.
If you use with('relation') => function ($query) { $query->get(['id']); } you'll get the whole object.
You have to include the foreign key columns. For the subsections relationship:
$q2->select('id', 'parent');

Mongodb nested document where clause returns many subdocuments

I have a mongodb document that looks similar to this:
{
"id": 1,
"title": "This is the title",
"body" : "This is the body",
"comments": [
{
"email_address": "mirko.benedetti#somemail.com",
"name": "Mirko",
"surname": "Benedetti",
"language": "it",
"text": "This is a message",
"published": "Y",
"on": "2014-03-22 15:04:04"
},
{
"email_address": "marc.surname#somemail.com",
"name": "Marc",
"surname": "Surname",
"language": "it",
"text": "Another Message",
"published": "N",
"on": "2014-03-23 15:04:05"
}
]
}
And I have a query like this:
$this->db->collection->find(array('id' => $id, 'language' => $lang, 'comments.published' => 'Y'),
array('comments.name' => 1, 'comments.surname' => 1, 'comments.text' => 1, 'comments.on' => 1, '_id' => 0));
My problem is that running that query, mongodb returns both comments, which I don't want, I want only the message with "published": "Y".
I tried for example to run 'comments.published' => 'something' and none comment is selected, which is correct, but if at least one of the comments has
the flag "published" set to 'Y', both comments are showed.
Any help will be welcome.
Look at $elemMatch documentation
db.schools.find( { zipcode: "63109" },
{ students: { $elemMatch: { school: 102 } } } )
You need to be careful while using the elemMatch operator. First thing it has two variants. $elemMatch(projection) & $elemMatch(query)
The elemMatch(projection) variant appears to working because the filter criteria you have only matches to one value in comments array.
The below query will work fine.
find({'_id' : ObjectId("582f2abf9b549b5a765ab380"), comments: { $elemMatch: { language: "it", published : "Y" }}})
Now consider when you have more than 1 matching values (two values with 'Y' published status) in comments arrays, then the above query will not work and will only return the first matching value.
In this scenario, you will need to use $filter, which will filter the comments array based on the filter crtieria passed.
aggregate([{
$match: {
'_id': ObjectId("582f2abf9b549b5a765ab380")
}
}, {
"$project": {
"comments": {
"$filter": {
"input": "$comments",
"as": "result",
"cond": {
$and: [{
$eq: ["$$result.language", "it"]
}, {
$eq: ["$$result.published", "Y"]
}]
}
}
}
}
}, {
$project: {
"comments": {
name: 1,
surname: 1,
text: 1,
on: 1
}
}
}])

Categories