我正在使用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
这正是我所追求的!
答案 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();