本文讲解如何在 Lara vel 中正确构建查询条件,使记录满足“大于等于开始时间、且仅当结束时间存在时小于等于结束时间”的逻辑,避免因 end_at 为 NULL 导致查询失效。

聊一个业务里经常碰到的“小坑”——时间范围筛选,但结束日期偏偏是可空的。比如活动有效期、价格策略、权限有效期这类场景,通常会有个必填的start_at,和一个可选的end_at。如果end_at为空,表示长期有效;不为空,那就得严格落在 [start_at, end_at] 这个闭区间内。听起来简单,写查询的时候却容易翻车。

直接上 whereBetween()?不行,因为那个方法默认两端都得非空,给个null进去,SQL 直接报错或者出假结果。写两个where条件固定住?更糟——where('end_at', '>=', $effectiveDate) 碰上 end_at 是 NULL,SQL 里 NULL 跟任何值比较都返回 UNKNOWN,整行记录就被过滤掉了,那些本该长期有效的记录全没了。

正确做法是条件化查询,用闭包把“或”逻辑包装进去。看这段代码:

$effectiveDate = '2022-04-06 23:34:00';
GroupExtras::query()
    ->select('group_extras.*')
    ->join('groups', 'group_extras.group_id', '=', 'groups.id')
    ->where('group_extras.start_at', '<=', $effectiveDate)
    ->where(function ($query) use ($effectiveDate) {
        // 分支一:end_at 为 NULL → 视为无限期,无需额外限制
        $query->whereNull('group_extras.end_at')
              // 分支二:end_at 非 NULL → 必须 >= effectiveDate
              ->orWhere(function ($sub) use ($effectiveDate) {
                  $sub->whereNotNull('group_extras.end_at')
                      ->where('group_extras.end_at', '>=', $effectiveDate);
              });
    })
    ->orderBy('group_extras.value')
    ->first();

关键逻辑拆开看:

另外有几个小提醒:

这个方案写起来语义清晰,也兼容主流数据库,算是 Lara vel 里处理“半开放时间区间”的标准思路了。下次遇到类似需求,直接套用这个闭包模板就行,不用再纠结 NULL 导致的“幽灵过滤”。

本文转载于:https://www.php.cn/faq/2323544.html 如有侵犯,请联系zhengruancom@outlook.com删除。
免责声明:正软商城发布此文仅为传递信息,不代表正软商城认同其观点或证实其描述。