Laravel多Where子句查询不符合预期:获取过期列表数据异常问题
问题分析与解决方案
嘿,我发现你当前的查询逻辑有个关键问题——你用了三个独立的where()闭包,它们之间是AND关系,这意味着只有同时满足三个条件的记录才会被筛选出来,但这完全不符合你需要的「优先级判断过期日期」的规则。
你的需求逻辑回顾
我们需要按优先级取第一个非空的过期日期来判断是否过期:
- 优先用
to_date_3(不为空时) to_date_3为空时用to_date_2(不为空时)- 前两个都为空时用
to_date_1
正确的查询写法(两种方案)
方案一:用查询构造器嵌套条件(逻辑清晰)
这种方式把每个优先级的判断封装成互斥的子条件,用orWhere()组合起来,完全贴合你的需求逻辑:
$query = Listing::query() ->where('vendor_id', $vendor->id) ->where('is_deleted', 0) ->where('is_published', 1) ->where('is_approved', 1) ->where(function ($q) { $currentDate = \Carbon\Carbon::now()->format('Y-m-d'); // 条件1:to_date_3不为空且已过期 $q->orWhere(function ($subQuery) use ($currentDate) { $subQuery->whereNotNull('to_date_3') ->where('to_date_3', '<', $currentDate); }) // 条件2:to_date_3为空,to_date_2不为空且已过期 ->orWhere(function ($subQuery) use ($currentDate) { $subQuery->whereNull('to_date_3') ->whereNotNull('to_date_2') ->where('to_date_2', '<', $currentDate); }) // 条件3:to_date_3和to_date_2都为空,to_date_1已过期 ->orWhere(function ($subQuery) use ($currentDate) { $subQuery->whereNull('to_date_3') ->whereNull('to_date_2') ->where('to_date_1', '<', $currentDate); }); }) ->orderBy('created_at', 'desc'); $listings = $query->paginate(10);
方案二:用SQL的COALESCE函数(简洁高效)
COALESCE()函数会返回传入参数中第一个非NULL的值,刚好匹配我们的优先级规则,代码会更简洁:
$query = Listing::query() ->where('vendor_id', $vendor->id) ->where('is_deleted', 0) ->where('is_published', 1) ->where('is_approved', 1) // 直接取第一个非空的过期日期,判断是否小于当前日期 ->whereRaw('COALESCE(to_date_3, to_date_2, to_date_1) < ?', [\Carbon\Carbon::now()->format('Y-m-d')]) ->orderBy('created_at', 'desc'); $listings = $query->paginate(10);
关于你示例数据的验证
按你给出的示例数据和当前日期2021-08-21:
- 记录1的
to_date_3是2021-09-15(未过期),不会被选中 - 记录2的
to_date_2是2021-08-22(未过期),不会被选中 - 记录3的
to_date_1是2021-08-20(已过期),会被选中
如果你的预期结果是记录1和2,可能需要检查示例数据里的日期是否符合「已过期」的条件哦~
内容的提问来源于stack exchange,提问作者Osama Shaki
相关产品推荐
相关产品推荐

