Laravel 雄辩加入 vs 内在加入?

2022-08-30 17:05:38

因此,我在弄清楚如何进行feed样式的mysql调用时遇到了一些麻烦,我不知道这是一个雄辩的问题还是mysql问题。我相信这在两者中都是可能的,我只是需要一些帮助。

所以我有一个用户,他们去他们的提要页面,在这个页面上,它显示他们朋友的东西(朋友投票,朋友评论,朋友状态更新)。所以假设我有汤姆,蒂姆和泰勒作为我的朋友,我需要得到他们所有的投票,评论,状态更新。我该怎么做?我有一个按ID号列出的所有朋友的列表,并且我有每个事件(投票,评论,状态更新)的表格,其中存储了Id以链接回用户。那么我怎么能一次获得所有这些信息,这样我就可以以形式在Feed中显示它。

蒂姆评论“酷”

泰勒说:“哇第一次状态更新~!

泰勒被评为“有史以来最佳比赛”

编辑@damiani 因此,在进行模型更改后,我有这样的代码,并且它确实返回了正确的行

$friends_votes = $user->friends()->join('votes', 'votes.userId', '=', 'friend.friendId')->orderBy('created_at', 'DESC')->get(['votes.*']);
$friends_comments = $user->friends()->join('comments', 'comments.userId', '=', 'friend.friendId')->orderBy('created_at', 'DESC')->get(['comments.*']);
$friends_status = $user->friends()->join('status', 'status.userId', '=', 'friend.friendId')->orderBy('created_at', 'DESC')->get(['status.*']);

但是我希望它们同时发生,这是因为mysql按顺序对数千条记录进行排序比php获取3个列表,合并它们然后执行此操作快100倍。有什么想法吗?


答案 1

我相信还有其他方法可以实现此目的,但一种解决方案是通过查询生成器使用。join

如果您有表设置如下设置:

users
    id
    ...

friends
    id
    user_id
    friend_id
    ...

votes, comments and status_updates (3 tables)
    id
    user_id
    ....

在您的用户模型中:

class User extends Eloquent {
    public function friends()
    {
        return $this->hasMany('Friend');
    }
}

在您的好友模型中:

class Friend extends Eloquent {
    public function user()
    {
        return $this->belongsTo('User');
    }
}

然后,若要收集 ID 为 1 的用户的朋友的所有投票,可以运行以下查询:

$user = User::find(1);
$friends_votes = $user->friends()
    ->with('user') // bring along details of the friend
    ->join('votes', 'votes.user_id', '=', 'friends.friend_id')
    ->get(['votes.*']); // exclude extra details from friends table

对 和 表运行相同的操作。如果您希望投票、评论和status_updates按时间顺序排列在一个列表中,则可以将生成的三个集合合并为一个集合,然后对合并的集合进行排序。joincommentsstatus_updates


编辑

若要在一个查询中获取投票、注释和状态更新,可以生成每个查询,然后合并结果。不幸的是,如果我们使用雄辩关系,这似乎不起作用(请参阅此问题的评论以讨论该问题),因此我们必须修改为查询以使用:hasManywhere

$friends_votes = 
    DB::table('friends')->where('friends.user_id','1')
    ->join('votes', 'votes.user_id', '=', 'friends.friend_id');

$friends_comments = 
    DB::table('friends')->where('friends.user_id','1')
    ->join('comments', 'comments.user_id', '=', 'friends.friend_id');

$friends_status_updates = 
    DB::table('status_updates')->where('status_updates.user_id','1')
    ->join('friends', 'status_updates.user_id', '=', 'friends.friend_id');

$friends_events = 
    $friends_votes
    ->union($friends_comments)
    ->union($friends_status_updates)
    ->get();

但是,在这一点上,我们的查询变得有点毛茸茸的,因此与和额外的表(如下面建议的 DefiniteIntegral)的多态关系可能是一个更好的主意。


答案 2

可能不是你想听到的,但是“feeds”表将是这种交易的一个很好的中间人,给你一种非规范化的方式,通过多态关系转向所有这些数据。

你可以这样构建它:

<?php

Schema::create('feeds', function($table) {
    $table->increments('id');
    $table->timestamps();
    $table->unsignedInteger('user_id');
    $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
    $table->morphs('target'); 
});

像这样构建源模型:

<?php

class Feed extends Eloquent
{
    protected $fillable = ['user_id', 'target_type', 'target_id'];

    public function user()
    {
        return $this->belongsTo('User');
    }

    public function target()
    {
        return $this->morphTo();
    }
}

然后使用如下内容保持最新状态:

<?php

Vote::created(function(Vote $vote) {
    $target_type = 'Vote';
    $target_id   = $vote->id;
    $user_id     = $vote->user_id;

    Feed::create(compact('target_type', 'target_id', 'user_id'));
});

您可以使上述内容更加通用/健壮 - 这仅用于演示目的。

此时,您的 Feed 项目非常容易一次性检索:

<?php

Feed::whereIn('user_id', $my_friend_ids)
    ->with('user', 'target')
    ->orderBy('created_at', 'desc')
    ->get();

推荐