Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Merge 'with' and 'whereHas' in Laravel 5

Tags:

I have this code in Laravel 5, using Eloquent, which is working perfectly:

$filterTask = function($query) use ($id) {     $query->where('taskid', $id); };  User::whereHas('submissions', $filterTask)->with(['submissions' => $filterTask])->get(); 

Basically the goal is to get only those users with their filtered submissions, which has any of them. However, it seems wasting to run both whereHas and with methods with the same callback function. Is there a way to simplify it?

Thanks.

like image 398
KristofMorva Avatar asked Apr 12 '15 16:04

KristofMorva


People also ask

What is with () in Laravel?

with() function is used to eager load in Laravel. Unless of using 2 or more separate queries to fetch data from the database , we can use it with() method after the first command. It provides a better user experience as we do not have to wait for a longer period of time in fetching data from the database.

What is Polymorphic relationship in Laravel?

A one-to-one polymorphic relationship is a situation where one model can belong to more than one type of model but on only one association. A typical example of this is featured images on a post and an avatar for a user. The only thing that changes however is how we get the associated model by using morphOne instead.

What is eager loading in Laravel?

Eager loading is super simple using Laravel and basically prevents you from encountering the N+1 problem with your data. This problem is caused by making N+1 queries to the database, where N is the number of items being fetched from the database.


2 Answers

In terms of performance you can't really optimize anything here (except if you were to move from eloquent relations to joins). With or without whereHas, two queries will be run. One to select all users another one to load the related models. When you add the whereHas condition a subquery is added, but it's still two queries.

However, syntactically you could optimize this a bit by adding a query scope to your model (or even a base model if you want to use this more often):

public function scopeWithAndWhereHas($query, $relation, $constraint){     return $query->whereHas($relation, $constraint)                  ->with([$relation => $constraint]); } 

Usage:

User::withAndWhereHas('submissions', function($query) use ($id){     $query->where('taskid', $id); })->get(); 
like image 199
lukasgeiter Avatar answered Sep 30 '22 07:09

lukasgeiter


The 'macroable' way (Laravel 5.4+)

Add this inside a service provider's boot() method.

\Illuminate\Database\Eloquent\Builder\Eloquent::macro('withAndWhereHas', function($relation, $constraint){     return $this->whereHas($relation, $constraint)->with([$relation => $constraint]); }); 
like image 21
Ijas Ameenudeen Avatar answered Sep 30 '22 07:09

Ijas Ameenudeen