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

Laravel中如何通过Eloquent多where条件实现单查询关联两张表

Laravel Eloquent 可用房间查询优化方案

你当前的实现存在两个可优化点:一是分两次查询产生了额外的数据库开销,二是Booking查询中的orWhereBetween未做条件分组,会导致SQL逻辑不符合预期,漏判部分时段重叠的预订记录。

推荐最佳实践:使用关联查询一次完成

Laravel Eloquent提供的whereDoesntHave方法可以直接基于关联关系过滤数据,仅需一次SQL查询即可拿到结果,性能和可读性都更好。

  • 先确认Room模型中已定义和Booking的一对多关联:
// app/Models/Room.php
public function bookings()
{
    return $this->hasMany(Booking::class);
}
  • 直接通过关联条件过滤无重叠预订的房间,这里用时段重叠的通用判断逻辑替代原来冗余且有漏洞的条件:

两个时段重叠的充要条件:已存在预订的开始时间早于查询时段的结束时间,且已存在预订的结束时间晚于查询时段的开始时间

$rooms = Room::whereDoesntHave('bookings', function ($q) use ($request) {
    $q->where('from', '<', $request->to)
      ->where('to', '>', $request->from);
})->get();

备选方案:子查询写法(无需定义关联)

如果你不想使用模型关联,也可以通过whereNotIn的闭包写法直接生成子查询,不需要单独查询预订ID集合再做二次过滤,同样可以把两次请求合并为一次:

$rooms = Room::whereNotIn('id', function ($q) use ($request) {
    $q->select('room_id')
      ->from('bookings')
      ->where('from', '<', $request->to)
      ->where('to', '>', $request->from);
})->get();

原代码逻辑问题说明

你原来写的时间条件存在逻辑漏洞:orWhereBetween没有包裹在闭包分组中,生成的SQL会把后续两个where条件仅作用在or分支的后半段,会漏掉「预订时段完全包裹查询时段」「预订时段完全落在查询时段内」等重叠场景,替换成上面的两个判断条件即可覆盖所有重叠情况。

内容的提问来源于stack exchange,提问作者Misha

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 18:00:52