Do you just want the banners with the right size and/or do you want to remove the advertisers when there is no banners with that size?
Nested BelongsToMany query - How to filter unwanted results
I'm trying to query all the advertisers assigned to a domain, and return all the ads assigned to these advertisers which match a set of user variables, and that have banner ads.
// Domain Model
public function advertisers()
{
return $this->belongsToMany('App\Advertiser');
}
// Ad Model
public function banners()
{
return $this->hasMany('App\Banner');
}
// the other methods are belongsToMany()
return $this->domain->load([
'advertisers' => function ($q) {
return $q->with('ads.banners')
->whereHas('ads.banners', function ($q) {
$q->where('size', '=', '728x90');
})
->with('ads.countries')
->whereHas('ads.countries', function ($q) {
$q->whereIn('key', [session('country_code'), 'XX']);
})
->with('ads.operatingSystems')
->whereHas('ads.operatingSystems', function ($q) {
$q->whereIn('key', [session('os'), 'all']);
})
->with('ads.deviceTypes')
->whereHas('ads.deviceTypes', function ($q) {
$q->whereIn('key', [session('device_type'), 'all']);
})
->with('ads.browsers')
->whereHas('ads.browsers', function ($q) {
$q->whereIn('key', [session('browser'), 'all']);
});
}
]);
The query almost works, but returns banner relationship with wrong sizes because they are part of the same advertiser.
#relations: array:5 [▼
"banners" => Collection {#542 ▼
#items: array:2 [▼
0 => banner {#582 ▶} // <-------- correct size 728x90
1 => banner {#583 ▼
#table: "banners"
+timestamps: true
#dates: array:1 [▶]
#connection: "mysql"
#primaryKey: "id"
#keyType: "int"
+incrementing: true
#with: []
#withCount: []
#perPage: 15
+exists: true
+wasRecentlyCreated: false
#attributes: array:7 [▼
"id" => 26
"created_at" => "2018-04-06 16:54:40"
"updated_at" => "2018-04-06 16:54:40"
"deleted_at" => null
"size" => "160x600" // <-------------------------- wrong size
"s3_path" => "/banner/xN0Q9g6t94not3WOTnZ8IWR58wSa0rxBQfMBJUA1.png"
"ad_id" => 1
]
#original: array:7 [▶]
#changes: []
#casts: []
#dateFormat: null
#appends: []
#dispatchesEvents: []
#observables: []
#relations: []
#touches: []
#hidden: []
#visible: []
#fillable: []
#guarded: array:1 [▶]
#forceDeleting: false
}
]
}
"countries" => Collection {#546 ▶}
"operatingSystems" => Collection {#553 ▶}
"deviceTypes" => Collection {#620 ▶}
"browsers" => Collection {#655 ▶}
How can I write the query so it only returns the banners with the size I requested?
Okay, after testing, and some more testing, and then testing another day I came up with the solution.
It is totally logical now that I think about it.
Because I am querying through multiple relationships, I have to add ->with() and ->whereHas() to every query scope in Eloquent.
Only by using both, and also in every scope I can have a resulting collection where the parents don't contain either empty results, or results with wrong sizes but instead only the correct sizes.
Now parents are not set if no sizes were found in the distant child and if a distant child has multiple sizes, also only the requested size is included in the result.
Please or to participate in this conversation.