Laravel多条件查询异常求助:结果不符合预期
Laravel订单查询功能异常排查
问题背景
使用Laravel开发3个月,实现前端表格订单查询时遇到两个核心问题:
- 原代码通过
orWhere组合三个查询条件(用户姓名匹配、课程订阅参与者匹配、帆船赛订阅参与者匹配),原本正常,但突然返回违反初始条件whereNot('status', 'pending')->has('items')的订单。 - 尝试拆分为三个独立查询
$ordersUsers、$ordersCourses、$ordersRegattas后,单独保留一个查询时结果正常,同时存在时要么结果为空,要么返回未调用的查询结果(如示例中最终赋值$orders = $ordersRegattas却返回$ordersUsers的结果)。
相关模型结构
- Order模型与OrderItem(items)为
hasMany关联,与User为hasOne关联 - OrderItem包含course/regatta类型,对应
courseSubscription和regattaSubscription关联 - 订阅模型包含参与者(attendees/regattaattendees),其中帆船赛参与者需满足
is_owner与owner_partecipate同时为0或1的条件
问题代码
$orders = Order::whereNot('status', 'pending')->has('items')->orderBy('updated_at', "desc"); $ordersUsers = $orders; $ordersCourses = $orders; $ordersRegattas = $orders; if($request->input('s')){ $ordersUsers = $ordersUsers->whereHas('user',function($query) use ($request){ $query->where('first_name', 'LIKE', '%'.$request->input('s').'%') ->orWhere('last_name','LIKE', '%' .$request->input('s') . '%')->orWhere( DB::raw("concat(first_name, ' ', last_name)"), 'LIKE', '%' .$request->input('s') . '%'); }); $ordersCourses = $ordersCourses->whereHas('items', function($query2) use ($request) { $query2->where('type', 'App\Models\Course')->whereHas('courseSubscription', function($query2) use ($request){ $query2->whereState(CourseSubscription::SUBSCRIPTION_COMPLETED) ->whereHas('attendees', function($query2) use ($request) { $query2->where('name', 'LIKE', '%' .$request->input('s') .'%') ->orWhere('surname','LIKE', '%' .$request->input('s') . '%')->orWhere( DB::raw("concat(name, ' ', surname)"), 'LIKE', '%' .$request->input('s') . '%'); }); }); }); $ordersRegattas = $ordersRegattas->whereHas('items', function($query3) use ($request) { $query3->whereType('App\Models\Regatta')->whereHas('regattaSubscription', function($query3) use ($request){ $query3->whereHas('regattaattendees', function($query3) use ($request) { $query3->where(function($query3) use ($request) { $query3->where('is_owner', 1)->where('owner_partecipate', 1)->where('name', 'LIKE', '%' .$request->input('s') . '%')->orWhere('surname','LIKE', '%' .$request->input('s') . '%')->orWhere( DB::raw("concat(name, ' ', surname)"), 'LIKE', '%' .$request->input('s') . '%'); })->orWhere(function($query3) use ($request) { $query3->where('is_owner', 0)->where('owner_partecipate', 0) ->where('name', 'LIKE', '%' .$request->input('s') . '%') ->orWhere('surname','LIKE', '%' .$request->input('s') . '%')->orWhere( DB::raw("concat(name, ' ', surname)"), 'LIKE', '%' .$request->input('s') . '%'); }); }); }); }); $orders = $ordersRegattas; } $orders = $orders->paginate(20); return view('society.orders.index', compact('orders'));
旧代码参考
$orders = Order::whereNot('status', 'pending')->has('items')->orderBy('updated_at', "desc"); if($request->input('s')){ $orders = $orders->whereHas('user',function($query) use ($request){ return $query->where('first_name', 'LIKE', '%' .$request->input('s') . '%')->orWhere('last_name','LIKE', '%' .$request->input('s') . '%')->orWhere( DB::raw("concat(first_name, ' ', last_name)"), 'LIKE', '%' .$request->input('s') . '%' );; })->orWhereHas('items', function($query) use ($request) { return $query->whereType('App\Models\Course')->whereHas('courseSubscription', function($query) use ($request){ return $query->whereState(CourseSubscription::SUBSCRIPTION_COMPLETED)->whereHas('attendees', function($query) use ($request) { return $query->where('name', 'LIKE', '%' .$request->input('s') . '%')->orWhere('surname','LIKE', '%' .$request->input('s') . '%')->orWhere( DB::raw("concat(name, ' ', surname)"), 'LIKE', '%' .$request->input('s') . '%' ); }); }); })->orWhereHas('items', function($query) use ($request) { return $query->whereType('App\Models\Regatta')->whereHas('regattaSubscription', function($query) use ($request){ return $query->whereState(RegattaSubscription::SUBSCRIPTION_COMPLETED)->whereHas('regattaattendees', function($query) use ($request) { return $query->where(function($query) use ($request) { return $query->where('is_owner', 1)->where('owner_partecipate', 1)->where('name', 'LIKE', '%' .$request->input('s') . '%')->orWhere('surname','LIKE', '%' .$request->input('s') . '%')->orWhere( DB::raw("concat(name, ' ', surname)"), 'LIKE', '%' .$request->input('s') . '%' ); })->orWhere(function($query) use ($request) { return $query->where('is_owner', 0)->where('owner_partecipate', 0)->where('name', 'LIKE', '%' .$request->input('s') . '%')->orWhere('surname','LIKE', '%' .$request->input('s') . '%')->orWhere( DB::raw("concat(name, ' ', surname)"), 'LIKE', '%' .$request->input('s') . '%' ); }); }); }); }); } $orders = $orders->paginate(20)->withQueryString(); return view('society.orders.index', compact('orders'));
问题原因与修复方案
1. 拆分查询时的核心问题:查询构建器引用传递
Laravel的查询构建器是引用类型,直接将$orders赋值给三个变量时,所有变量指向同一个查询实例。修改任意一个变量的条件,都会同步修改原始实例,导致条件叠加混乱。
修复方式:使用clone关键字创建独立查询实例:
$orders = Order::whereNot('status', 'pending')->has('items')->orderBy('updated_at', "desc"); $ordersUsers = clone $orders; $ordersCourses = clone $orders; $ordersRegattas = clone $orders;
2. 原代码orWhere导致初始条件失效
原代码中orWhereHas会破坏初始条件的逻辑优先级:OR逻辑会让数据库忽略前面的AND条件,只要满足任意一个OR分支就返回结果,包括状态为pending或无items的订单。
修复方式:将所有查询分支包裹在一个where闭包内,确保初始条件始终生效:
$orders = Order::whereNot('status', 'pending')->has('items')->orderBy('updated_at', "desc"); if($request->input('s')){ $search = $request->input('s'); $orders->where(function($query) use ($search) { // 用户姓名匹配 $query->whereHas('user',function($q) use ($search){ $q->where('first_name', 'LIKE', "%{$search}%") ->orWhere('last_name','LIKE', "%{$search}%") ->orWhere(DB::raw("concat(first_name, ' ', last_name)"), 'LIKE', "%{$search}%"); }) // 课程订阅参与者匹配 ->orWhereHas('items', function($q) use ($search) { $q->whereType('App\Models\Course') ->whereHas('courseSubscription', function($q) use ($search){ $q->whereState(CourseSubscription::SUBSCRIPTION_COMPLETED) ->whereHas('attendees', function($q) use ($search) { $q->where('name', 'LIKE', "%{$search}%") ->orWhere('surname','LIKE', "%{$search}%") ->orWhere(DB::raw("concat(name, ' ', surname)"), 'LIKE', "%{$search}%"); }); }); }) // 帆船赛订阅参与者匹配 ->orWhereHas('items', function($q) use ($search) { $q->whereType('App\Models\Regatta') ->whereHas('regattaSubscription', function($q) use ($search){ $q->whereHas('regattaattendees', function($q) use ($search) { $q->where(function($q) use ($search) { $q->where('is_owner', 1)->where('owner_partecipate', 1) ->where(function($q) use ($search) { $q->where('name', 'LIKE', "%{$search}%") ->orWhere('surname','LIKE', "%{$search}%") ->orWhere(DB::raw("concat(name, ' ', surname)"), 'LIKE', "%{$search}%"); }); })->orWhere(function($q) use ($search) { $q->where('is_owner', 0)->where('owner_partecipate', 0) ->where(function($q) use ($search) { $q->where('name', 'LIKE', "%{$search}%") ->orWhere('surname','LIKE', "%{$search}%") ->orWhere(DB::raw("concat(name, ' ', surname)"), 'LIKE', "%{$search}%"); }); }); }); }); }); }); } $orders = $orders->paginate(20)->withQueryString(); return view('society.orders.index', compact('orders'));
3. 帆船赛参与者查询的逻辑漏洞
原代码中帆船赛参与者的条件存在逻辑错误:is_owner和owner_partecipate的判断后直接跟orWhere,会导致条件变成(is_owner=1 AND owner_partecipate=1 AND name like ...) OR surname like ...,不符合需求。需将姓名匹配条件包裹在子闭包内,确保is_owner和owner_partecipate的条件始终生效。
内容的提问来源于stack exchange,提问作者LordMregal
相关产品推荐
相关产品推荐

