Laravel 5 belongsToMany 搜索使用 pivot table
Laravel 5 belongsToMany search using pivot table
我正在使用 Laravel 的 belongsToMany
和一个枢轴 table 到 link 模型。但是,我使用的范围界定创建了一个非常低效的查询。这是我的代码:
Restaurant
class
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
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 将合并所有 table 的所有列,所以我也这样做了:
$sql = Restaurant::select('restaurants.*')->byCity($city_id)->byCuisine($cuisine_id)->toSql
这正是我所追求的!
您可以调整查询范围以使用连接:
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.*')
,然后访问 cuisine 和 city eloquent 对象通过使用 with('cuisine', 'city')
预先加载它们
一起:
$restaurants = Restaurant::select('restaurant.*')
->city(4)
->cuisine(3)
->with('cuisine', 'city')
->get();
我正在使用 Laravel 的 belongsToMany
和一个枢轴 table 到 link 模型。但是,我使用的范围界定创建了一个非常低效的查询。这是我的代码:
Restaurant
class
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
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 将合并所有 table 的所有列,所以我也这样做了:
$sql = Restaurant::select('restaurants.*')->byCity($city_id)->byCuisine($cuisine_id)->toSql
这正是我所追求的!
您可以调整查询范围以使用连接:
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.*')
,然后访问 cuisine 和 city eloquent 对象通过使用 with('cuisine', 'city')
一起:
$restaurants = Restaurant::select('restaurant.*')
->city(4)
->cuisine(3)
->with('cuisine', 'city')
->get();