简体   繁体   中英

In Laravel 4 how to query ManyToMany relationships?

In Laravel 4 I have the following tables

 - items table
 --- id
 --- name


 - tags table
 --- id
 --- name

 - item_tag
 --- id
 --- tag_id
 --- item_id
 --- created_at
 --- updated_at



class Item extends Eloquent {

    public function tags()
    {
        return $this->belongsToMany('Tag');
    }
}





class Tag extends Eloquent {

    public function items()
    {
       return $this->hasMany('Item');
    }
}

my question:

I want to get all items that have the following two tags "foo" and "bar"? only items that have both tags should be returned!?

update

I have tried the below but it did not work for me, I have a feeling that the problem is with the "->having" clause but I could not get it right,

lets asume that the tag "foo" has the id 1 and "bar" has id 2

class Item extends Eloquent {

protected $table = 'items';

public function tags()
{
    return $this->belongsToMany('Tag');
}

 public static function withTags()
{
  return static::leftJoin(
    'item_tag',
    'items.id', '=', 'item_tag.item_id'
  )
  ->whereIn('item_tag.tag_id', array(1, 2))
   ->groupBy('items.id')
   ->having('count(*)', '=',2)
  ;
}   
}

and to run it

   #routes.php
   Route::get('/', function()
   {
        return Item::withTags()->get();
   });

it should return all items with that tags 1 and 2 however it is not returning anything!

any help?

Finally I have found the answer!

using "havingRaw" will solve the problem

also "lists" give us the IDs of the tags from tags table

note: "havingRaw" is available only in Laravel 4 -beta 4- or later

class Item extends Eloquent {

protected $table = 'items';

public function tags()
{
    return $this->belongsToMany('Tag');
}

public static function withTags($tags = array())
{
    $count = count($tags);
    return static::leftjoin('item_tag', 'items.id', '=', 'item_tag.item_id')
        ->whereIn('item_tag.tag_id', Tag::whereIn('name', $tags)->lists('id'))
        ->groupBy('item_tag.item_id')
        ->havingRaw('count(*)='.$count)
        ;
}    
}

and to run it

   return Item::withTags(['foo','bar'])->get();

Update:Important Note

when you see the output of the code above you will notice that item->id does not contain the items.id!, instead it will contain the tags.id, this is because using "joins" will cause ambiguity, to solve this you have to add the following select statement

  ->select('items.id as id','items.name as name')

For a ManyToMany relationship, both Eloquent classes need to return a $this->belongsToMany . In your Tag class you are using hasMany which does not use the pivot table.

If you fix the above, then you should be able to properly access the pivot table.

I think the problem is that you need to join on the tags id instead of the items id.

return static::leftJoin(
    'item_tag',
    'tags.id', '=', 'item_tag.tag_id'
)

I guess you are looking for a inner join? try this:

class Item extends Eloquent {

    protected $table = 'items';

    public function tags()
    {
        return $this->belongsToMany('Tag');
    }

    public static function withTags()
    {
        return static::join('item_tag', 'items.id', '=', 'item_tag.item_id')
            ->whereIn('item_tag.tag_id', array(1, 2));
    }   
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM