【问题标题】:Am I doing eager loading correctly? (Eloquent)我是否正确地进行急切加载? (雄辩)
【发布时间】:2018-06-09 00:46:33
【问题描述】:

我有一个方法需要从三个相关模型中提取信息。我有一个可行的解决方案,但我担心我仍然遇到 N+1 查询问题(也在寻找有关如何检查我是否渴望正确加载的解决方案)。

这三个模型是 Challenge、Entrant、User。

挑战模型包含:

 /**
 * 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 次。欢迎对代码提出任何提示或建议,以及检查预加载是否正常工作的方法。

【问题讨论】:

  • 如果您只想知道您的预加载是否有效,只需在使用-&gt;with('entrants.user') 之后转储变量$challenges 并且不使用该方法。并检查结果。dd($challenges)

标签: php laravel orm eloquent


【解决方案1】:

使用 Laravel Debugbar 检查您的 Laravel 应用程序为每个请求创建的查询。

您的 Eloquent 查询应该只生成 3 个原始 SQL 查询,并且您需要确保此行不会生成 N 个额外的查询:

$entrants = $challenge->entrants->load('user')->sortByDesc('current_total_amount')->all()

【讨论】:

  • 哇,调试器很糟糕,额外的加载语句添加了很多查询,谢谢(我什至发现了很多其他额外的查询,我需要用那个调试器摆脱)
【解决方案2】:

当您执行-&gt;with('entrants.user') 时,一旦您到达-&gt;get(),它就会同时加载参赛者和用户。当您执行-&gt;load('user') 时,它会运行另一个查询来获取用户。但是您不需要这样做,因为您在运行-&gt;with('entrants.user') 时已经将其拉出。

如果您使用 -&gt;loadMissing('user') 而不是 -&gt;load('user') 它应该可以防止冗余调用。

但是,如果您利用 Collection methods,您可以在声明 $challenges 的开头运行第一个查询:

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...

您不需要使用-&gt;load('user'),因为$challenge-&gt;entrants 已经填充了参赛者和相关用户。所以你可以利用 Collection 方法-&gt;sortByDesc() 在 php 中对列表进行排序。

另外,您不需要运行-&gt;all(),因为这会将其转换为模型数组(您可以将其保留为模型集合并仍然 foreach 它)。

【讨论】:

  • 刚刚取出该 load 语句显着减少了我的查询,但 loadMissing 方法出现错误,我使用的是 5.5,可能他们弃用了吗?
  • 老实说,我从未尝试过 loadMissing。但是我在加载的文档中看到了它。我需要做更多的实验来了解它的价值以及如何使用它
猜你喜欢
  • 2013-09-22
  • 1970-01-01
  • 2019-09-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多