Laravel Eloquent Join vs Inner Join?
Posted
技术标签:
【中文标题】Laravel Eloquent Join vs Inner Join?【英文标题】:Laravel Eloquent Join vs Inner Join? 【发布时间】:2014-11-17 00:26:00 【问题描述】:所以我在弄清楚如何进行提要风格的 mysql 调用时遇到了一些麻烦,我不知道这是一个雄辩的问题还是 mysql 的问题。我相信这两者都是可能的,我只是需要一些帮助。
所以我有一个用户,他们转到他们的提要页面,在这个页面上显示来自他们朋友的东西(朋友投票、朋友 cmet、朋友状态更新)。所以说我有汤姆、蒂姆和泰勒作为我的朋友,我需要得到他们所有的选票、cmets、状态更新。我该怎么做?我有一个按 ID 号列出的所有朋友的列表,并且我有每个事件(投票、cmets、状态更新)的表,其中存储了 ID 以链接回用户。那么我怎样才能一次获得所有这些信息,以便我可以将其显示在提要中。
蒂姆评论“酷”
Taylor 说“Woot first status update~!”
Taylor 被评为“有史以来最好的比赛”
编辑@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 倍。有什么想法吗?
【问题讨论】:
您如何存储此list of all the friends by Id number
.... 是在friends
表中,还是在users
表的列中以逗号分隔的列表?如果是后者,则规范化您的数据库,以便您可以使用模型关系 (hasManyThrough())
您可以在您的数据库中创建一个视图,并为它创建一个 Eloquent model
。然后只需获取您想要的所有事件。否则你可以使用polymorphic
关系:laravel.com/docs/eloquent#polymorphic-relations
请不要在原问题上添加正确答案,那样会变得很模糊
【参考方案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
对comments
和status_updates
表运行相同的join
。如果您希望 votes、cmets 和 status_updates 位于一个按时间顺序排列的列表中,您可以将生成的三个集合合并为一个,然后对合并的集合进行排序。
编辑
要在一个查询中获取投票、cmets 和状态更新,您可以构建每个查询,然后合并结果。不幸的是,如果我们使用 Eloquent hasMany
关系(see comments for this question 用于讨论该问题),这似乎不起作用,因此我们必须修改查询以使用 where
代替:
$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 建议)可能是一个更好的主意。
【讨论】:
也许我做错了什么,但如果我运行这段代码,我最终会得到 id 为 1 的用户的所有信息,而不是朋友的信息。 经过一些调整,我最终得到了这个工作,所以你说如果我想获得 cmets、状态更新等我应该运行单独的调用,有没有办法一次获得所有这些? 我使用union
编辑了答案以包含一次获得所有3个的代码。【参考方案2】:
可能不是您想听到的,但“提要”表将是此类事务的出色中间人,为您提供一种非规范化的方式,通过多态关系转向所有这些数据。
你可以这样构建它:
<?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'));
);
您可以使上述内容更加通用/稳健——这仅用于演示目的。
此时,您的提要项目真的很容易一次检索:
<?php
Feed::whereIn('user_id', $my_friend_ids)
->with('user', 'target')
->orderBy('created_at', 'desc')
->get();
【讨论】:
另请注意...从技术上讲,如果您在道德上反对附加表,则可以使用联合查询来执行此操作。就个人而言,我认为非规范化是一种强大的技术,适用于这种情况...... UNION 版本会更加粗糙。以上是关于Laravel Eloquent Join vs Inner Join?的主要内容,如果未能解决你的问题,请参考以下文章
Laravel Eloquent LEFT JOIN WHERE NULL
Laravel Eloquent 关系——相当于 MYSQL “join”
Laravel Eloquent ORM 中的 join()->where()