问题
I have Jobs and Projects in my Laravel site and there is a relationship between them. I'm trying to reduce the number of queries made (and be mindful of N+1 problem). I do this with eager loading in my controller when outputting my JobItems.
How can I do this when I nest: JobItems > Project > JobItems.
There is one instance where this happens and I am getting duplicate queries. I'm not sure how to eager load. I'll outline the situation:
Each JobItem has a column for hours.
In my project I SUM all associated JobItem hours to determine the total hours in a project. (Eg. 3 JobItems with 4hours each, I then have a an accessor in my Project Model that says projectHours = 12).
- job
- project
- project_hours (SUM of associated jobs)
- project
I want to list all JobItems and each JobItem to have a child element of the related Project.
So I am calling:
- JobItem Model
- Project Model (to put as a child in JobItem)
- JobItem Model (to calculate total hours in Project Model)
At this step 3 I am getting N+1 issue and multiple duplicate queries. I'd like to reduce this with eager loading, but not sure how if I am (in step 1) already calling the JobItem model.
In my controller I have:
public function getJobItems()
{
$userId = auth()->user()->id;
return JobItem::whereHas('project', function ($query) use ($userId) {
$query->where('user_id', '=', $userId);
})->with(['project', 'user'])
->get();
}
In my Project model I have:
class Project extends Model
{
protected $appends = ['projectHours'];
public function jobs()
{
return $this->hasMany('App\JobItem', 'project_id', 'id');
}
public function getProjectHoursAttribute()
{
return $this->jobs->sum('hours');
}
}
As always, if I am approaching this incorrectly let me know. Much appreciated.
NB. This is related to this SO ticket about reducing queries with Eager Loading: How to use eager loading on Laravel model with SUM of a relationship - currently getting multiple queries (N+1)
回答1:
I'm going to answer this in two parts:
1) Preloading Relationships
So if you are try to list out a jobItem
collection but want to display an attribute of each item's associated Project
's Job
s, you can preload the required relationships like so:
JobItem::with(['project.jobs'])->get();
The .
operator can be used to access nested relationships. (It follows the same rules as loading any relationship in that it's the name of the relationship on the respective model)
2) Accessing Relationships
I notice that you are constraining a query by a user id even though you have a User
model available. Provided you have a jobItems
relationship on you User
model, you could do this instead:
public function getJobItems()
{
$user = auth()->user()->loadMissing(['jobItems.project.jobs']);
return $user->jobItems;
}
来源:https://stackoverflow.com/questions/60354755/eager-loading-on-laravel-query-when-nested-3-deep