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

时间:2015-10-06 17:40:53

标签: php mysql laravel eloquent laravel-5.1

我正在尝试使用Eloquent为Laravel中的帖子构建一个简单的新闻源。

我基本上想要检索所有帖子 ...

  • 我是作者
  • 我追随的人是作者(可跟随)
  • 我关注的人对此进行了评论
  • 其中具有相同field_id的人是作者
  • 其中有相同school_id的人是作者

在一个查询中。

由于我从未对加入/组合的SQL查询进行过强烈的工作,因此非常感谢您对此的任何帮助!

我的桌子

users

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

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

followables

+-------------+---------------+---------------+-----------------+
| 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');
    }
}

2 个答案:

答案 0 :(得分:1)

您可以尝试对此查询使用左连接,但它会变得很复杂,因为您必须使用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();

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

所以它会像..

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

获得不同的查询后,无法获得结果,您可以将它们联合起来..

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

希望这会引导你朝着正确的方向前进

答案 1 :(得分:1)

好的,所以你想要检索上述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');

让我们定义每个条件:

我是作者

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

我关注的人是作者(可跟随)

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

我关注的人对此进行了评论
这个实际上有点棘手:我们需要使用->whereHas构造,它找到至少有1个与子查询匹配的注释的帖子。

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

剩下的两个很简单。

具有相同field_id的人是作者

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

你可以看到它的发展方向

具有相同school_id的人是作者

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

要获得匹配的帖子,您只需要

$posts = $query->get();

在这种特殊情况下,从头开始构建查询时,如果您的需求发生变化,它将创建一个相当脆弱的结构。为了增加灵活性,您可能希望在PostComments模型上为每个组件构建query scopes。这意味着您只需要弄清楚一个用户关注的帖子由其创建的帖子意味着什么,然后您只需Post::authoredBySomeoneFollowedByUser($me)来获取您关注的人撰写的帖子集合。< / p>