在Laravel/Eloquent中查询多个嵌套关系


Query multiple, nested Relationships in Laravel/Eloquent

我正在尝试用Eloquent为Laravel中的帖子建立一个简单的新闻提要。

我基本上想检索所有Posts

  • where I am author
  • 我关注的人是作者(可关注)
  • ,我关注的人已经评论了
  • ,具有相同field_id的人是作者
  • ,具有相同school_id的人是作者

由于我从未密集地使用过连接/组合SQL查询,因此非常感谢对此的任何帮助!

我的表

users table

+----+
| id |
+----+

posts

+----+-----------+-------------+
| id | author_id | author_type |
|----|-----------|-------------|
|    | users.id  | 'App'User'  |
+----+-----------+-------------+

comments

+----+----------------+------------------+-----------+-------------+
| id | commentable_id | commentable_type | author_id | author_type |
|----|----------------|------------------|-----------|-------------|
|    | posts.id       | 'App'Post'       | users.id  | 'App'User'  |
+----+----------------+------------------+-----------+-------------+

schoolables table

+---------+-----------+----------+
| user_id | school_id | field_id |
+---------+-----------+----------+

followables table

+-------------+---------------+---------------+-----------------+
| follower_id | follower_type | followable_id | followable_type |
|-------------|---------------|---------------|-----------------|
| users.id    | 'App'User'    | users.id      | 'App'User'      |
+-------------+---------------+---------------+-----------------+

我的模型
class Post extends Model
{
    /**
     * @return 'Illuminate'Database'Eloquent'Relations'MorphTo
     */
    public function author()
    {
        return $this->morphTo();
    }
    /**
     * @return 'Illuminate'Database'Eloquent'Relations'MorphMany
     */
    public function comments()
    {
        return $this->morphMany(Comment::class, 'commentable');
    }
}
class Comment extends Model
{
    /**
     * @return 'Illuminate'Database'Eloquent'Relations'MorphTo
     */
    public function author()
    {
        return $this->morphTo();
    }
    /**
     * @return 'Illuminate'Database'Eloquent'Relations'MorphTo
     */
    public function commentable()
    {
        return $this->morphTo();
    }
}
class User extends Model
{
    /**
     * @return 'Illuminate'Database'Eloquent'Relations'MorphMany
     */
    public function posts()
    {
        return $this->morphMany(Post::class, 'author')->orderBy('created_at', 'desc');
    }
    /**
     * @return 'Illuminate'Database'Eloquent'Relations'MorphMany
     */
    public function comments()
    {
        return $this->morphMany(Comment::class, 'author')->orderBy('created_at', 'desc');
    }
    /**
     * @return 'Illuminate'Database'Eloquent'Relations'BelongsToMany
     */
    public function schoolables()
    {
        return $this->hasMany(Schoolable::class);
    }
    /**
     * @return 'Illuminate'Database'Eloquent'Relations'MorphMany
     */
    public function following()
    {
        return $this->morphMany(Followable::class, 'follower');
    }
}

您可以尝试在此查询中使用左连接,但这将成为一件复杂的事情,因为您必须使用leftJoins进行所有连接,然后在所有

上嵌套orWhere子句
$posts = Post::leftJoin('..', '..', '=', '..')
  ->where(function($query){
    $query->where('author_id', Auth::user()->id); // being the author
  })->orWhere(function($query){
    // second clause...
  })->orWhere(function($query){
    // third clause...
  .....
  })->get();

我不认为这将是可管理的,所以我建议使用union, http://laravel.com/docs/5.1/queries#unions

就像…

$written = Auth::user()->posts();
$following = Auth::user()->following()->posts();

在得到不同的查询后,不需要得到结果,就可以把它们统一起来。

$posts = $written->union($following)->get();

希望这能指引你走向正确的方向

好的,那么您想要检索符合上述5个条件中的任何一个的所有帖子。编写此类查询的技巧是将它们分解成更小、更易于管理的部分。

$query = Post::query();

假设你是$me

您所关注的用户id可以通过

获取
$followingUserIds = $me
    ->following()
    ->where('followable_type', User::class)
    ->lists('followable_id');

使用

可以获得与您相同字段的用户id。
$myFieldIds = $me->schoolables()->lists('field_id');
$sharedFieldUserIds = Schoolable::whereIn('field_id', $myFieldIds)->lists('user_id');

同样,与您在同一学校的用户可以使用

获得
$mySchoolIds = $me->schoolables()->lists('school_id');
$sharedSchoolUserIds = Schoolable::whereIn('school_id', $mySchoolIds)->lists('user_id');

让我们定义这些条件:

Where I am author

$query->where(function($inner) use ($me) {
    $inner->where('posts.author_type', User::class);
    $inner->where('posts.author_id', $me->id);
});

我关注的人都是author (followable)

$query->orWhere(function($inner) use ($followingUserIds) {
    $inner->where('posts.author_type', User::class);
    $inner->whereIn('posts.author_id', $followingUserIds);
});

我关注的人评论的地方
这个实际上有点棘手:我们需要使用->whereHas结构,它查找至少有一条评论与子查询匹配的帖子。

$query->orWhereHas('comments', function($subquery) use ($followingUserIds) {
    $subquery->where('comments.author_type', User::class);
    $subquery->whereIn('comments.author_id', $followingUserIds);
});

其余两个很简单。

$query->orWhere(function($inner) use ($sharedFieldUserIds) {
    $inner->where('posts.author_type', User::class);
    $inner->whereIn('posts.author_id', $sharedFieldUserIds);
});

你可以看到这是怎么回事

$query->orWhere(function($inner) use ($sharedSchoolUserIds) {
    $inner->where('posts.author_type', User::class);
    $inner->whereIn('posts.author_id', $sharedSchoolUserIds);
});

要获得匹配的帖子,只需要执行

$posts = $query->get();

虽然从头开始构建查询在这种特殊情况下有效,但如果您的需求发生变化,它将创建一个相当脆弱的结构。为了增加灵活性,您可能希望在PostComments模型上为每个组件构建查询范围。这意味着您只需要计算一次用户关注的人撰写的帖子意味着什么,然后您可以简单地执行Post::authoredBySomeoneFollowedByUser($me)来获取您关注的人撰写的帖子的集合。