我有一个方法需要从三个相关模型中提取信息。我有一个有效的解决方案,但我恐怕仍然在运行N+ 1查询问题(也在寻找如何检查我是否急于加载正确的解决方案)。
这三种模式分别是挑战型、进入型、用户型。
挑战模型包含:
/**
* Retrieves the Entrants object associated to the Challenge
* @return \Illuminate\Database\Eloquent\Relations\HasMany
*/
public function entrants()
{
return $this->hasMany('App\Entrant');
}
参赛者模型包括:
/**
* Retrieves the Challenge object associated to the Entrant
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function challenge()
{
return $this->belongsTo('App\Challenge', 'challenge_id');
}
/**
* Retrieves the User object associated to the Entrant
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function user()
{
return $this->belongsTo('App\User', 'user_id');
}
用户模型包括:
/**
* Retrieves the Entrants object associated to the User
* @return \Illuminate\Database\Eloquent\Relations\HasMany
*/
public function entrants()
{
return $this->hasMany('App\Entrant');
}
我尝试使用的紧急加载方法如下:
/**
* Returns an array of currently running challenges
* with associated entrants and associated users
* @return array
*/
public function liveChallenges()
{
$currentDate = Carbon::now();
$challenges = Challenge::where('end_date', '>', $currentDate)
->with('entrants.user')
->where('start_date', '<', $currentDate)
->where('active', '1')
->get();
$challengesObject = [];
foreach ($challenges as $challenge) {
$entrants = $challenge->entrants->load('user')->sortByDesc('current_total_amount')->all();
$entrantsObject = [];
foreach ($entrants as $entrant) {
$user = $entrant->user;
$entrantsObject[] = [
'entrant' => $entrant,
'user' => $user
];
}
$challengesObject[] = [
'challenge' => $challenge,
'entrants' => $entrantsObject
];
}
return $challengesObject;
}
我觉得我遵循了文档建议:https://laravel.com/docs/5.5/eloquent-relationships#eager-loading
但不确定如何检查以确保我没有进行n+1查询,而不是仅进行2查询。欢迎对代码提出任何提示或建议,以及检查紧急加载是否正常工作的方法。
最佳答案
当您执行->with('entrants.user')
时,当您到达->get()
时,它将同时加载进入者和用户。当您执行->load('user')
时,它会运行另一个查询来获取用户。但您不需要这样做,因为您在运行->with('entrants.user')
时已经拉过它。
如果您使用->loadMissing('user')
而不是->load('user')
,则应防止冗余调用。
但是,如果你使用Collection methods,你可以在刚开始的1个查询中逃跑,在那里你声明“cc>:
foreach ($challenges as $challenge) {
// at this point, $challenge->entrants is a Collection because you already eager-loaded it
$entrants = $challenge->entrants->sortByDesc('current_total_amount');
// etc...
您不需要使用
$challenges
,因为->load('user')
已经填充了参赛者和相关用户。因此,您可以利用集合方法$challenge->entrants
对PHP中的列表进行排序。此外,您不需要运行
->sortByDesc()
,因为这样可以将它转换成一个模型数组(您可以将其保持为模型集合,并且仍然保留它)。