Laravel - 预加载

Laravel - Eager Loading

我正在尝试使用 Laravel 来理解预加载以避免生成大量不必要的查询。我想获得最后添加的 15 个 Posts 并从我的费率 table 的关系中获得他们的费率(在我得到 Posts 之前,后来在 foreach 中我要求 $item- >avgRate() 创建了 15 个额外的查询 :S).

我的Post模特:

public function rates()
{
    return $this->hasMany(Rate::class);
}

public function scopeLastAdded($query, $limit = 15)
{
    return $query->latest()->limit($limit)->with('rates')->get();
}

这有效,对于每个 post,我也得到所有费率,但主要目标是创建一些函数来计算每个 post 的平均费率,而不是检索所有费率。我创建了一个新方法:

public function avgRate()
{
    return number_format($this->rates()->avg('rate'), 1, '.', '');
}

当我使用 with('avgRate') 时,我的模型失败了:

Call to a member function addEagerConstraints() on string

我怎样才能用我最后的 15 Post 以某种干净的方式获得 avgRate 以仅执行 2 个查询而不是 16 个?

预期输出:

// Post view
@foreach ($posts as $post)
   <div>{{ $post->title }}</div>
   <div>{{ $post->avgRate }}</div> //I want to get data without performing 15 queries
@endforeach

我会使用子查询来实现这一点。此外,为了让事情更清晰一些,您可以创建一个范围来获取评分:

public function scopeWithRating($query)
{
    $rating = Rate::selectRaw('AVG(rate)')
        ->whereColumn('post_id', 'posts.id')
        ->getQuery();

    $query->select('posts.*')
        ->selectSub($rating, 'rating');
}

...要使用它,您需要:

Post::withRating()->get(); 

现在,您的 Post 对象也将包含一列 rating,并且基本上是通过单个查询完成的。

这里有一个 example 来说明这一点。