Eloquent wherein with join multiple tables

依然范特西╮ 提交于 2019-12-25 08:48:03

问题


I have 4 tables, items, listings, catitem_item, and item_listing.

items and listing is many to many relationship. items and catitems is also many to many relationship.

catitems contains list of item's categories. listings is like location where items located. example listing shop A can have item chair and item chair have multiple catitem categories.

My goal is to get items which under list of categories such as category 1 AND 2 AND 3 ($cats) and with listing information where this item located. So if there are 6 listings for chair, it will return 6 chair results.

This is the query I have so far.

$items = DB::table('items')
                ->join('catitem_item', 'catitem_item.item_id', '=', 'items.id')
                ->join('item_listing', 'item_listing.item_id', '=', 'items.id')
                ->join('listings', 'item_listing.listing_id', '=', 'listings.id')
                ->whereIn('catitem_item.catitem_id', $cats)
                //->groupBy('items.id')
                //->having(DB::raw('count(*)'), '=', count($cats))
                ->select('items.id', 'items.name', 'items.price', 'items.slug', 'item_listing.listing_id', 'listings.name as listing_name', 'listings.slug as listing_slug')
                ->get();

回答1:


Note that the way you are trying to do it, you might get multiple rows per item (once per related listing). A better way would be to have an array of listings per item.

If you use eloquent models and you have setup the relations correctly, you could try the following:

$cats = [1, 2, 3];

$query = Item::with('listings');
foreach ($cats as $cat) {
    $query->whereHas('catitems', function($q) use($cat) {
        $q->where('id', $cat);
    });
}
$items = $query->get();

Now every item shoud have a listings property. For example for the first item you can access the listings the following way:

$item1 = $items[0];
$listings1 = $item1->listings;

Note that whereHas() will probably create a correlated EXISTS subquery for every entry in the $cats array. If that is to slow, you can use a JOIN query like:

$items = Item::with('listings')
    ->join('catitem_item', 'catitem_item.item_id', '=', 'items.id')
    ->whereIn('catitem_item.catitem_id', $cats)
    ->groupBy('items.id')
    ->having(DB::raw('count(*)'), '=', count($cats))
    ->select('items.*')
    ->get();

If you don't use eloquent, you can also do the "eager loading" yourself.

$items = DB::table('items')
    ->join('catitem_item', 'catitem_item.item_id', '=', 'items.id')
    ->whereIn('catitem_item.catitem_id', $cats)
    ->groupBy('items.id')
    ->having(DB::raw('count(*)'), '=', count($cats))
    ->select('items.*')
    ->get()
    ->keyBy('id');

foreach ($items as $item) {
    $item->listings = [];
}

$itemIds = $items->pluck('id');
$listings = DB::table('listings')
    ->join('item_listing', 'item_listing.listing_id', '=', 'listings.id')
    ->whereIn('item_listing.item_id', $itemIds)
    ->groupBy('listings.id')
    ->select('listings.*', DB::raw('group_concat(item_listing.item_id) as item_ids'))
    ->get();

foreach ($listings as $listing) {
    $itemIds = explode(',', $listing->item_ids);
    foreach ($itemIds as $itemId) {
        $items[$itemId]->listings[] = $listing;
    }
    $listing->forget('item_ids');
}


来源:https://stackoverflow.com/questions/45597329/eloquent-wherein-with-join-multiple-tables

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!