如何优化校验日期范围是否已被占用的Eloquent查询
Eloquent查询优化方案
- 第一阶段:抽离公共条件简化冗余代码
你原有代码中三个OR子句都包含状态非取消的判断,可以直接把该条件提到最外层全局生效,同时可以用whereBetween简化日期区间判断,优化后代码如下:
$bookings = Booking::where('status', '!=', Booking::STATUS_CANCELED) ->where(function ($q) use ($start, $end) { $q->whereBetween('started_at', [$start, $end]) ->orWhereBetween('ends_at', [$start, $end]) ->orWhere(fn ($q) => $q->where('started_at', '<', $start)->where('ends_at', '>', $end)); })->get();
- 第二阶段:简化时间重叠判断逻辑
判断两个时间段是否存在重叠有通用的简化公式,不需要拆分三种场景:只要已有预约的开始时间 < 传入结束时间,且已有预约的结束时间 > 传入开始时间,即判定为重叠。如果你的业务允许边界时间重合(如前一预约结束时间等于后一预约开始时间不算冲突),可以把运算符调整为<=/>=,优化后代码如下:
$bookings = Booking::where('status', '!=', Booking::STATUS_CANCELED) ->where('started_at', '<', $end) ->where('ends_at', '>', $start) ->get();
该逻辑和你原有实现完全等价,代码量大幅缩减。
- 进阶优化:复用查询逻辑
如果这类判断在项目中多处使用,可以在Booking模型中定义本地作用域,后续调用更简洁易维护:
首先在模型文件中注册作用域:
// app/Models/Booking.php class Booking extends Model { const STATUS_CANCELED = 'canceled'; // 你的原有状态定义 public function scopeNotCanceled($query) { return $query->where('status', '!=', self::STATUS_CANCELED); } public function scopeOverlapping($query, $start, $end) { return $query->where('started_at', '<', $end) ->where('ends_at', '>', $start); } }
业务代码直接调用即可:
$bookings = Booking::notCanceled()->overlapping($start, $end)->get();
小提示:原代码中使用的
!==运算符在Eloquent查询构造器中推荐替换为标准的!=,避免部分数据库驱动解析异常。
内容的提问来源于stack exchange,提问作者Kylian
相关产品推荐
相关产品推荐

