Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Where NOT in pivot table

In Laravel we can setup relationships like so:

class User {
    public function items()
    {
        return $this->belongsToMany('Item');
    }
}

Allowing us to to get all items in a pivot table for a user:

Auth::user()->items();

However what if I want to get the opposite of that. And get all items the user DOES NOT have yet. So NOT in the pivot table.

Is there a simple way to do this?

like image 976
Rob Avatar asked Nov 20 '13 07:11

Rob


4 Answers

Looking at the source code of the class Illuminate\Database\Eloquent\Builder, we have two methods in Laravel that does this: whereDoesntHave (opposite of whereHas) and doesntHave (opposite of 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();

this works correctly for me!

For simple "Where not exists relationship", use this:

User::doesntHave('Role')->get();

Sorry, do not understand English. I used the google translator.

like image 118
Wallace Maxters Avatar answered Nov 01 '22 16:11

Wallace Maxters


For simplicity and symmetry you could create a new method in the User model:

// User model
public function availableItems()
{
    $ids = \DB::table('item_user')->where('user_id', '=', $this->id)->lists('user_id');
    return \Item::whereNotIn('id', $ids)->get();
}

To use call:

Auth::user()->availableItems();
like image 28
Makita Avatar answered Nov 01 '22 16:11

Makita


It's not that simple but usually the most efficient way is to use a subquery.

$items = Item::whereNotIn('id', function ($query) use ($user_id)
    {
        $query->select('item_id')
            ->table('item_user')
            ->where('user_id', '=', $user_id);
    })
    ->get();

If this was something I did often I would add it as a scope method to the Item model.

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

}

Then use that later like this.

$items = Item::whereNotRelatedToUser($user_id)->get();
like image 6
Collin James Avatar answered Nov 01 '22 15:11

Collin James


How about left join?

Assuming the tables are users, items and item_user find all items not associated with the user 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();
like image 2
mp31415 Avatar answered Nov 01 '22 17:11

mp31415