Laravel中按order_id分组查询Eloquent关联pivot中间表值的实现方法
问题解答
你可以通过优化Eloquent查询+简单的集合处理实现需求,不需要复杂的额外逻辑。同时你当前的实现存在两处明显问题:
- Dish和Restaurant是多对多关联,dishes表不存在
restaurant_id字段,你写的Dish::where('restaurant_id', $user_id)逻辑不成立,只有一对多关联才会在子表存父级ID。 - 循环获取订单属于典型的N+1查询,数据量稍大就会出现严重性能问题。
优化实现步骤
第一步:先补全Order模型的关联配置
你需要在Order的dishes关联中声明要读取的中间表字段,否则无法获取quantity值:
class Order extends Model { public function dishes() { return $this->belongsToMany('App\Models\Dish')->withPivot('quantity'); } }
第二步:改写控制器查询逻辑
public function index($restaurantId) { $orders = Order::whereHas('dishes.restaurant', function ($query) use ($restaurantId) { // 筛选出包含当前餐厅菜品的订单 $query->where('restaurants.id', $restaurantId); }) ->with(['dishes' => function ($query) use ($restaurantId) { // 预加载订单中属于当前餐厅的菜品,过滤其他餐厅的菜品 $query->whereHas('restaurant', function ($q) use ($restaurantId) { $q->where('restaurants.id', $restaurantId); }); }]) ->get() ->unique('id') // 订单去重,避免同一个订单因关联多个菜品被重复返回 ->map(function ($order) { // 整理菜品输出结构 $order->dishes = $order->dishes->map(function ($dish) { return [ 'dish_id' => $dish->id, 'quantity' => $dish->pivot->quantity ]; }); return $order; }) ->values(); return response()->json($orders); }
以上代码总共只执行3次数据库查询,完全避免了N+1问题,输出结构和你要求的格式完全一致。
这类场景的最佳实践
- 优先用预加载(with方法) 读取关联数据,绝对不要在循环中执行数据库查询。
- 多对多关联的条件筛选优先用
whereHas实现,比先查关联模型再循环取数的效率高一个数量级。 - 输出结构统一用Laravel的API资源(API Resource) 管理,不要把结构整理逻辑写在控制器里,后续维护更方便,示例:
// app/Http/Resources/OrderResource.php class OrderResource extends JsonResource { public function toArray($request) { return [ 'id' => $this->id, 'status' => $this->status, 'address' => $this->address, 'user_name' => $this->user_name, 'user_surname' => $this->user_surname, 'phone' => $this->phone, 'email' => $this->email, 'total' => $this->total, 'created_at' => $this->created_at, 'updated_at' => $this->updated_at, 'dishes' => DishResource::collection($this->whenLoaded('dishes')) ]; } }
控制器中直接返回资源集合即可:return OrderResource::collection($orders);
- 如果你的业务逻辑中一个菜品仅属于一个餐厅,建议把Dish和Restaurant的关联改成一对多(Dish表加
restaurant_id字段,关联改成belongsTo),查询效率会更高,逻辑也更清晰。
内容的提问来源于stack exchange,提问作者BigOoga
相关产品推荐
相关产品推荐

