Dans Laravel, nous pouvons configurer des relations comme suit:
class User {
public function items()
{
return $this->belongsToMany('Item');
}
}
Nous permettant d'obtenir tous les éléments d'un tableau croisé dynamique pour un utilisateur:
Auth::user()->items();
Cependant, si je veux obtenir le contraire de cela. Et obtenez tous les éléments que l’utilisateur NE A PAS encore. Donc, pas dans le tableau croisé dynamique.
Y a-t-il un moyen simple de faire cela?
Pour plus de simplicité et de symétrie, vous pouvez créer une nouvelle méthode dans le modèle User:
// User model
public function availableItems()
{
$ids = \DB::table('item_user')->where('user_id', '=', $this->id)->lists('user_id');
return \Item::whereNotIn('id', $ids)->get();
}
Pour utiliser call:
Auth::user()->availableItems();
En regardant le code source de la classe Illuminate\Database\Eloquent\Builder
, nous avons deux méthodes dans Laravel qui fait ceci: whereDoesntHave
(contraire de whereHas
) et doesntHave
(contraire de has
)
// SELECT * FROM users WHERE ((SELECT count(*) FROM roles WHERE user.role_id = roles.id AND id = 1) < 1) AND ...
User::whereDoesntHave('Role', function ($query) use($id) {
$query->whereId($id);
})
->get();
cela fonctionne correctement pour moi!
Pour une relation simple "Où n'existe pas existe", utilisez ceci:
User::doesntHave('Role')->get();
Désolé, je ne comprends pas l'anglais. J'ai utilisé le traducteur google.
Ce n'est pas si simple, mais le moyen le plus efficace consiste généralement à utiliser une sous-requête.
$items = Item::whereNotIn('id', function ($query) use ($user_id)
{
$query->select('item_id')
->table('item_user')
->where('user_id', '=', $user_id);
})
->get();
Si c'était quelque chose que je faisais souvent, je l'ajouterais comme méthode d'étendue au modèle Item.
class Item extends Eloquent {
public function scopeWhereNotRelatedToUser($query, $user_id)
{
$query->whereNotIn('id', function ($query) use ($user_id)
{
$query->select('item_id')
->table('item_user')
->where('user_id', '=', $user_id);
});
}
}
Puis utilisez ça plus tard comme ça.
$items = Item::whereNotRelatedToUser($user_id)->get();
Que diriez-vous de gauche rejoindre?
En supposant que les tables sont users
, items
et item_user
rechercher toutes les items
non associées à l'utilisateur 123
:
DB::table('items')->leftJoin(
'item_user', function ($join) {
$join->on('items.id', '=', 'item_user.item_id')
->where('item_user.user_id', '=', 123);
})
->whereNull('item_user.item_id')
->get();
A fini par écrire une portée pour ceci comme ceci:
public function scopeAvail($query)
{
return $query->join('item_user', 'items.id', '<>', 'item_user.item_id')->where('item_user.user_id', Auth::user()->id);
}
Et puis appelez:
Items::avail()->get();
Fonctionne pour le moment, mais un peu brouillon. Aimeriez-vous voir quelque chose avec un mot clé comme not
:
Auth::user()->itemsNot();
De manière générale, Eloquent exécute quand même la requête ci-dessus, sauf avec un =
au lieu d'un <>
.
Peut-être que vous pouvez utiliser:
DB::table('users')
->whereExists(function($query)
{
$query->select(DB::raw(1))
->from('orders')
->whereRaw('orders.user_id = users.id');
})
->get();
cela devrait fonctionner pour vous
$someuser = Auth::user();
$someusers_items = $someuser->related()->lists('item_id');
$all_items = Item::all()->lists('id');
$someuser_doesnt_have_items = array_diff($all_items, $someusers_items);