首页 > 解决方案 > 在 laravel 中过滤关系

问题描述

我有具有 hasMany 关系的 Posts 和 Comments 模型:

public function comments()
{
    return $this->hasMany(Posts::class, 'posts_id', 'id');
}

在我的控制器中,我需要获取所有已发布的帖子(is_published = 1),以及所有已发布的评论,至少有 1 条已发布的评论:

$dbRecords = Posts::all()->whereStrict('is_published', 1);
$posts = [];
foreach ($dbRecords as $post) {
    if (count($post->comments()) === 0) {
        continue;
    }

    foreach ($post->comments() as $comment) {
        if ($comment->is_published === 1) {
            $posts[] = $post;

            continue(2); // to the next post
        }
    }
}

但是,这样的解决方案是丑陋的。此外,我将获得所有已发布的帖子,包括已发布和未发布的评论,因此我将被迫再次在资源中过滤评论。

我发现的另一个解决方案 - 使用原始查询:

$dbRecords = DB::select("SELECT posts.* 
    FROM posts
    JOIN comments ON posts_id = posts.id
    WHERE posts.is_published = 1
      AND comments.is_published = 1
    HAVING count(posts.id) > 0;");
$users = array_map(function($row) { return (new Posts)->forceFill($row); }, $dbRecords);

但它并没有解决需要过滤资源中未发布评论的问题。

标签: phplaravel

解决方案


  • 在 Laravel eloquent 中使用 Eager loading 来消除n+1查询问题。with
  • 用于查询关系存在haswhereHas功能。

在你的情况下,它会是这样的:

// Retrieve all posts that have at least one comment
$posts = Post::has('comments')->with('comments')->get();

// Retrieve posts with at least one comment and which are published
$callback = function($query) {
    $query->where('is_published ', '=', '1');
}

$posts = Post::whereHas('comments', $callback)
    ->with(['comments' => $callback])
    ->where('is_published ', '=', '1')
    ->get();

推荐阅读