Mysql Laravel多对多(在同一用户表/模型上):查询范围以包含指定用户的相关信息
用户可以相互阻止。一个用户可以阻止多个(其他)用户,一个用户可以被多个(其他)用户阻止。 在Mysql Laravel多对多(在同一用户表/模型上):查询范围以包含指定用户的相关信息,mysql,laravel,eloquent,laravel-query-builder,eloquent-relationship,Mysql,Laravel,Eloquent,Laravel Query Builder,Eloquent Relationship,用户可以相互阻止。一个用户可以阻止多个(其他)用户,一个用户可以被多个(其他)用户阻止。 在User模型中,我有以下多对多关系: /** * Get the users that are blocked by $this user. * * @return \Illuminate\Database\Eloquent\Relations\BelongsToMany */ public function blockedUsers() { return $this->belong
User
模型中,我有以下多对多关系:
/**
* Get the users that are blocked by $this user.
*
* @return \Illuminate\Database\Eloquent\Relations\BelongsToMany
*/
public function blockedUsers()
{
return $this->belongsToMany(User::class, 'ignore_lists', 'user_id', 'blocked_user_id');
}
/**
* Get the users that blocked $this user.
*
* @return \Illuminate\Database\Eloquent\Relations\BelongsToMany
*/
public function blockedByUsers()
{
return $this->belongsToMany(User::class, 'ignore_lists', 'blocked_user_id', 'user_id');
}
(ignore\u lists
是透视表,它有id
,user\u id
,“blocked\u user\u id”
列)
我想创建以下查询范围:
1)包括被指定用户($id
)阻止的用户:
用法示例:User::areBlockedBy(auth()->id())->where('verified',1)->get()代码>
2)要包括指定用户($id
)未阻止的用户,请执行以下操作:
用法示例:User::areNotBlockedBy(auth()->id())->where('verified',1)->get()代码>
3)要包括阻止指定用户的用户($id
):
用法示例:User::whoBlocked(auth()->id())->where('verified',1)->get()代码>
4)要包括未阻止指定用户的用户($id
):
用法示例:User::whoDidNotBlock(auth()->id())->where('verified',1)->get()代码>
你会怎么做?
我在网上找不到这方面的任何信息(也许我错过了)。
(我使用的是Laravel6.x)
我不确定,但我认为这可以通过两种方式来实现:使用左连接或在其中使用原始查询。。。我可能错了,但我认为“左连接”解决方案在性能方面会更好,对吗?(对此不确定,可能我完全错了)。使用连接(内部连接)
性能优于其中的子查询
在MySQL中,In子句中的子选择将针对外部查询中的每一行重新执行,从而创建O(n^2)
我认为使用whereHas
和whereDoesntHave
进行查询会更具可读性
1) 关系方法blockedUsers()
已经包含被指定的用户($id)
阻止的用户,您可以直接使用此方法:
User::where('id',$id)->first()->blockedUsers();
首先考虑应用where('verified',1)
,这样您就可以使用类似User::where('verified',1)->areBlockedBy(auth()->id())
这样的查询,范围可以如下所示:
公共函数作用域被($query,$id)阻止
{
返回$query->whereHas('blockedByUsers',函数($users)use($id){
$users->where('ignore_lists.user_id',$id);
});
}
//更好的性能:但是,当应用另一个where条件时,需要指定表名->where('users.verified',1)
公共函数作用域被($query,$id)阻止
{
返回$query->join('ignore_list',函数($q)use($id){
$q->on('ignore_list.blocked_user_id'、'='、'users.id')
->其中('ignore_lists.user_id',$id);
})->选择('users.*')->distinct();
}
我们对第二个查询使用join
,这将提高性能,因为它不需要在存在的地方使用
用户表中300000多条记录的示例:
解释第一个查询whereHas
,该查询扫描301119+1+1
行并获取575ms
:
解释第二个查询join
,它扫描3+1
行并取10.1ms
:
2) 要包含未被指定的用户($id)
阻止的用户,您可以使用whereDoesntHave
闭包,如下所示:
公共函数scopeNotBlockedUsers($query,$id)
{
返回$query->whereDoesntHave('blockedByUsers',函数($users)use($id){
$users->where('ignore_lists.user_id',$id);
});
}
我更喜欢使用whereDoesntHave
而不是leftJoin
。因为当您使用leftjoin
时,如下所示:
User::leftjoin('ignore_list',函数($q)use($id){
$q->on('ignore_list.blocked_user_id'、'='、'users.id')
->其中('ignore_lists.user_id',$id);
})->whereNull('ignore_lists.id')->select('users.*')->distinct()->get();
Mysql需要创建一个临时表来存储所有用户的记录,并合并一些忽略列表
,然后扫描这些记录,找出没有忽略列表
的记录whereDosentHave
也将扫描所有用户。对于我的mysql服务器,where not exists
比left join
快一点。它的执行计划似乎很好。这两个查询的性能差别不大。
对于whereDoesntHave
更具可读性。我将选择wheredonethave
。
3) 要包含阻止指定用户($id)
的用户,请使用其中有阻止的用户,如下所示:
公共函数($query,$id)
{
返回$query->whereHas('blockedUsers',函数($q)use($id){
$q->where('ignore_list.blocked_user_id',$id);
});
}
//更好的性能:但是,当应用另一个where条件时,需要指定表名->where('users.verified',1)
公共函数($query,$id)
{
返回$query->join('ignore_list',函数($q)use($id){
$q->on('ignore_lists.user_id'、'='、'users.id')
->其中('ignore_lists.blocked_user_id',$id);
})->选择('users.*')
/**
* Scope a query to only include users that are blocked by the specified user.
*
* @param \Illuminate\Database\Eloquent\Builder $query
* @param $id
* @return \Illuminate\Database\Eloquent\Builder
*/
public function scopeAreBlockedBy($query, $id)
{
// How to do this? :)
}
/**
* Scope a query to only include users that are not blocked by the specified user.
*
* @param \Illuminate\Database\Eloquent\Builder $query
* @param $id
* @return \Illuminate\Database\Eloquent\Builder
*/
public function scopeAreNotBlockedBy($query, $id)
{
// How to do this? :)
}
/**
* Scope a query to only include users that blocked the specified user.
*
* @param \Illuminate\Database\Eloquent\Builder $query
* @param $id
* @return \Illuminate\Database\Eloquent\Builder
*/
public function scopeWhoBlocked($query, $id)
{
// How to do this? :)
}
/**
* Scope a query to only include users that did not block the specified user.
*
* @param \Illuminate\Database\Eloquent\Builder $query
* @param $id
* @return \Illuminate\Database\Eloquent\Builder
*/
public function scopeWhoDidNotBlock($query, $id)
{
// How to do this? :)
}
/**
* Scope a query to only include users that are blocked by the specified user.
*
* @param \Illuminate\Database\Eloquent\Builder $query
* @param $id
* @return \Illuminate\Database\Eloquent\Builder
*/
public function scopeAreBlockedBy($query, $id)
{
return User::whereHas('blockedByUsers', function($q) use($id) {
$q->where('user_id', $id);
});
}
User::areBlockedBy(auth()->id())->where('verified', 1)->get();
/**
* Scope a query to only include users that are not blocked by the specified user.
*
* @param \Illuminate\Database\Eloquent\Builder $query
* @param $id
* @return \Illuminate\Database\Eloquent\Builder
*/
public function scopeAreNotBlockedBy($query, $id)
{
// It will exclude the user with $id
return User::where('id', '!=', $id)
->whereDoesntHave('blockedByUsers', function($q) use($id) {
$q->where('user_id', $id);
});
}
User::areNotBlockedBy(auth()->id())->where('verified', 1)->get();
/**
* Scope a query to only include users that blocked the specified user.
*
* @param \Illuminate\Database\Eloquent\Builder $query
* @param $id
* @return \Illuminate\Database\Eloquent\Builder
*/
public function scopeWhoBlocked($query, $id)
{
return User::whereHas('blockedUsers', function($q) use($id) {
$q->where('blocked_user_id', $id);
});
}
User::whoBlocked(auth()->id())->where('verified', 1)->get();
/**
* Scope a query to only include users that did not block the specified user.
*
* @param \Illuminate\Database\Eloquent\Builder $query
* @param $id
* @return \Illuminate\Database\Eloquent\Builder
*/
public function scopeWhoDidNotBlock($query, $id)
{
// It will exclude the user with $id
return User::where('id', '!=', $id)
->whereDoesntHave('blockedUsers', function($q) use($id) {
$q->where('blocked_user_id', $id);
});
}
User::whoDidNotBlock(auth()->id())->where('verified', 1)->get();