Laravel 5使用数据透视表属于ToMany搜索

时间:2015-12-22 00:14:28

标签: laravel many-to-many laravel-5.1

我正在使用Laravel的belongsToMany和数据透视表来链接模型。但是,我正在使用的范围创建一个非常低效的查询。这是我的代码:

Restaurant上课

class Restaurant extends Model {
    public function cuisines() {
        return $this->belongsToMany('Cuisine');
    }

    public function scopeByCity($query, $city_id) {
        return $query->where('city_id' '=', $id);
    }

    public function scopeByCuisine($query, $cuisine_id) {
        return $query->whereHas('cuisines', function($q) use ($cuisine_id) {
            $q->where('id', '=', $cuisine_id);
        });
    }
}

Cuisine上课

class Cuisine extends Model {
    public function restaurants() {
        return $this->belongsToMany('Restaurant');
    }
}

现在Restaurant::byCity(1)->byCuisine(2)->toSql()给了我:

select * from `restaurants` where `city_id` = ? and (select count(*) from `cuisines` inner join `restaurants_cuisines` on `cuisines`.`id` = `restaurants_cuisines`.`cuisine_id` where `restaurants_cuisines`.`restaurant_id` = `restaurants`.`id` and `id` = ?) >= 1

执行时间比优化后的查询长3倍:

select * from `restaurants` left join `restaurants_cuisines` on `restaurants`.`id` = `restaurants_cuisines`.`restaurant_id` left join `cuisines` on `cuisines.id` = `restaurants_cuisines`.`cuisine_id` where `restaurants`.`city_id` = ? and `cuisines`.`id` = ?

这是Laravel的查询构建器的限制还是我做错了?

更新 我现在已经将@Zoe Blair的答案标记为正确答案,但我仍然需要根据需要对其进行修改。对于处于类似情况的任何人来说,最终解决方案就是:

public function scopeByCuisine($query, $cuisine=null) {
    return $query->leftJoin('restaurants_cuisines', 'restaurants.id', '=', 'restaurants_cuisines.restaurant_id')
        ->leftJoin('cuisines', 'cuisines.id', '=', 'restaurants_cuisines.cuisine_id')
        ->where('cuisines.id', '=', $cuisine);
}

正如她在回答中所建议的那样,Laravel会将所有表中的所有列组合在一起,所以我也做了:

$sql = Restaurant::select('restaurants.*')->byCity($city_id)->byCuisine($cuisine_id)->toSql

这正是我所追求的!

1 个答案:

答案 0 :(得分:1)

您可以调整查询范围以使用连接:

public function scopeByCity($query, $city_id) {
    return $query->leftJoin('cities', 'cities.id', '=', 'restaurants.city_id')
           ->where('city_id' '=', $city_id);
}

public function scopeByCuisine($query, $cuisine_id) {
    return $query->leftJoin('cuisines', 'cuisines.id', '=', 'restaurants.cuisine_id')
           ->where('cuisine_id' '=', $cuisine_id);
}

我相信eloquent默认返回所有列,所以在调用查询范围之前我会将其限制为Restaurant::select('restaurants.*'),然后通过使用with('cuisine', 'city')急切加载它们来访问美食和城市雄辩的对象

一起:

$restaurants = Restaurant::select('restaurant.*')
    ->city(4)
    ->cuisine(3)
    ->with('cuisine', 'city')
    ->get();