Laravel muchos-a-muchos (sobre la mesa mismos usuarios / modelo): Pregunta alcances para incluir relacionados para el usuario especificado

PeraMika:

Los usuarios pueden bloquear entre sí. Un usuario puede bloquear muchos (otros) los usuarios, y un usuario puede ser bloqueada por muchos (otros) usuarios. En Userel modelo que tengo estos muchos-a-muchos relación:

/**
 * 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_listsEs la tabla de pivote y tiene id, user_id, 'blocked_user_id'columnas)

Quiero crear la siguiente consulta de Scopes :

1) Para incluir los usuarios que están bloqueados por el usuario especificado ( $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? :)
}

Ejemplo de uso: User::areBlockedBy(auth()->id())->where('verified', 1)->get();

2) Para incluir usuarios que están no bloqueados por el usuario especificado ( $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? :)
}

Ejemplo de uso: User::areNotBlockedBy(auth()->id())->where('verified', 1)->get();

3) Para incluir los usuarios que bloqueaban el usuario especificado ( $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? :)
}

Ejemplo de uso: User::whoBlocked(auth()->id())->where('verified', 1)->get();

4) Para incluir los usuarios que no bloquean el usuario especificado ( $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? :)
}

Ejemplo de uso: User::whoDidNotBlock(auth()->id())->where('verified', 1)->get();


¿Cómo harías esto? No he encontrado nada en los documentos laravel de internautas (tal vez me lo perdí). (Estoy usando laravel 6.x )

No estoy seguro, pero creo que esto podría hacerse de dos maneras: el uso de Ingreso izquierda o el uso de consultas primas en el que ... Puedo estar equivocado, pero creo que la "combinación izquierda" solución sería mejor en cuanto a rendimiento se refiere, ¿verdad? (no estoy seguro acerca de esto, tal vez estoy totalmente equivocado).

Tsakhog:

Uso join(inner join)rendimiento es mejor que whereInsubconsulta.

En MySQL, subselects dentro de la cláusula IN son re-ejecutado por cada fila de la consulta externa, creando de esta manera O(n^2).

Creo que el uso whereHasy whereDoesntHavepara la consulta será más fácil de leer.

1) El método de relación blockedUsers()ya se incluyen los usuarios que están bloqueados por la especificada user ($id), puede utilizar este método directamente:

User::where('id', $id)->first()->blockedUsers();

Considerado sobre la aplicación de la where('verified', 1)en un principio, por lo que se puede utilizar como consulta User::where('verified', 1)->areBlockedBy(auth()->id()), el alcance puede ser como la siguiente:

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();
}

Utilizamos joinpara la segunda consulta que mejorará el rendimiento, ya que no necesita el uso where exists.

Ejemplo de 300.000 registros en tabla de usuarios:

Explicar la primera consulta whereHas, que escanear 301119+1+1filas y toma 575ms:whereHas explicar tiempos whereHas

Explicar la segunda consulta join, que escanear 3+1filas y toma 10.1ms:unirse a explicar Únete a tiempo

2) Para incluir a los usuarios que no son bloqueados por la especificada user ($id), puede utilizar whereDoesntHaveel cierre como éste:

public function scopeNotBlockedUsers($query, $id)
{
    return $query->whereDoesntHave('blockedByUsers', function($users) use ($id){
           $users->where('ignore_lists.user_id', $id);
     });
}

Yo prefiero usar whereDoesntHaveen lugar de leftJoinaquí. Porque cuando se utiliza leftjoincomo esta a continuación:

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. whereDoesntHave explicar izquierda se unen es nulo

For whereDoesntHave is more readable. I will choose whereDoesntHave. whereDoesntHave y leftjoin

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.

Supongo que te gusta

Origin http://43.154.161.224:23101/article/api/json?id=372668&siteId=1
Recomendado
Clasificación