ユーザーがお互いをブロックすることができます。一のユーザは、ブロックすることができ、多くの(他の)ユーザーを、1人のユーザがによって遮断することができる多くの(他の)ユーザ。で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_lists
ピボットテーブルであり、それは持っているid
、user_id
、'blocked_user_id'
列)
私は、次の作成するクエリスコープ:
1)ユーザーが含まれるようにされている指定されたユーザ(によってブロックを$id
):
/**
* 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? :)
}
使用例: User::areBlockedBy(auth()->id())->where('verified', 1)->get();
2)しているユーザーに含まれない指定されたユーザ(によってブロックを$id
):
/**
* 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? :)
}
使用例: User::areNotBlockedBy(auth()->id())->where('verified', 1)->get();
3)ユーザーが含まれるようにブロックされた指定されたユーザを($id
):
/**
* 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? :)
}
使用例: User::whoBlocked(auth()->id())->where('verified', 1)->get();
4)ユーザー含めるにはブロックされませんでした指定されたユーザを($id
):
/**
* 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? :)
}
使用例: User::whoDidNotBlock(auth()->id())->where('verified', 1)->get();
あなたはこれをどのように行うのでしょうか?私は中に何も見つかりませんでしたLaravelドキュメントこれについては(多分私はそれを逃しました)。(私が使用していますLaravel 6.xのを)
私はわからないんだけど、私は、これは2つの方法で行うことができると思います:使用左が参加または使用して生のクエリをしてここ ...私は間違っているかもしれないが、私が思うにソリューションは限りのパフォーマンスとして良いだろう「左結合します」 、右懸念していますか?(これについてはよく分からない、多分私は完全に間違っています)。
使用join(inner join)
パフォーマンスがより優れているwhereIn
サブクエリ。
MySQLでは、IN句内の副選択は、このように作成し、外部クエリ内のすべての行に対して再実行されますO(n^2)
。
私が使用することを考えるwhereHas
とwhereDoesntHave
、クエリのために読みやすくなります。
1)関係法は、blockedUsers()
すでにユーザーが含まれていますブロックされている指定されたことでuser ($id)
、あなたは、このメソッドを直接使用することができました:
User::where('id', $id)->first()->blockedUsers();
適用について思いやりwhere('verified', 1)
のようなクエリを使用することができますので、最初のUser::where('verified', 1)->areBlockedBy(auth()->id())
範囲がこのようにすることができ、:
public function scopeAreBlockedBy($query, $id)
{
return $query->whereHas('blockedByUsers', function($users) use($id) {
$users->where('ignore_lists.user_id', $id);
});
}
// better performance: however, when you apply another where condition, you need to specify the table name ->where('users.verified', 1)
public function scopeAreBlockedBy($query, $id)
{
return $query->join('ignore_lists', function($q) use ($id) {
$q->on('ignore_lists.blocked_user_id', '=', 'users.id')
->where('ignore_lists.user_id', $id);
})->select('users.*')->distinct();
}
私たちは、使用join
それが使用する必要がないため、パフォーマンスが向上します2番目のクエリのためにwhere exists
。
ユーザーテーブル内の300,000レコードの例:
最初のクエリ説明whereHas
スキャン301119+1+1
行となります575ms
:
2番目のクエリ説明join
スキャン3+1
行となります10.1ms
:
2)ユーザーが含まれるようにすることによってブロックされていない指定されuser ($id)
、あなたが使用することができwhereDoesntHave
、この1のような閉鎖を:
public function scopeNotBlockedUsers($query, $id)
{
return $query->whereDoesntHave('blockedByUsers', function($users) use ($id){
$users->where('ignore_lists.user_id', $id);
});
}
私が使用することを好むwhereDoesntHave
の代わりに、leftJoin
ここに。あなたが使用するときため、leftjoin
以下のように:
User::leftjoin('ignore_lists', function($q) use ($id) {
$q->on('ignore_lists.blocked_user_id', '=', 'users.id')
->where('ignore_lists.user_id', $id);
})->whereNull('ignore_lists.id')->select('users.*')->distinct()->get();
Mysql need to create an temporary table for storing all the users' records and combine some ignore_lists
.And then scan these records and find out the records which without ignore_lists
. whereDosentHave
will scan all users too. For my mysql server, where not exists
is a little faster than left join
. Its execution plan seems good. The performance of these two queries are not much different.
For whereDoesntHave
is more readable. I will choose whereDoesntHave
.
3) To include users that blocked the specified user ($id)
, to use whereHas
blockedUsers like this:
public function scopeWhoBlocked($query, $id)
{
return $query->whereHas('blockedUsers', function($q) use ($id) {
$q->where('ignore_lists.blocked_user_id', $id);
});
}
// better performance: however, when you apply another where condition, you need to specify the table name ->where('users.verified', 1)
public function scopeWhoBlocked($query, $id)
{
return $query->join('ignore_lists', function($q) use ($id) {
$q->on('ignore_lists.user_id', '=', 'users.id')
->where('ignore_lists.blocked_user_id', $id);
})->select('users.*')->distinct();
}
4) To include users that did not block the specified user ($id)
, use whereDoesntHave
for blockedByUsers:
public function scopeWhoDidNotBlock($query, $id)
{
return $query->whereDoesntHave('blockedUsers', function($q) use ($id) {
$q->where('ignore_lists.blocked_user_id', $id);
});
}
PS: Remember to add index on foreign_key
for ignore_lists
table.