You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.18 02:12:11