Laravel Where查询中如何引用其他列值筛选进行中游戏回合
问题根因
原写法失效的核心问题是运算层级搞错了:
Carbon::now()->subMinutes()是PHP层面的逻辑,在SQL语句发送给数据库之前就已经执行完成,这时候还没读取rounds表中任意一行的duration字段值- 代码中裸写的
duration属于PHP上下文的未定义常量,根本不会映射到数据库的duration字段,完全无法实现逐行动态计算
你需要把「开始时间+持续时长」的运算下推到SQL层执行,让数据库在遍历每一行回合记录时,用该行自己的duration值计算结束时间,再和当前时间比对。
可直接复用的实现代码
根据你使用的数据库类型,选对应的写法即可:
MySQL/MariaDB 环境(最常用)
直接用DATE_ADD函数做时间偏移计算,参数绑定防注入:
// 查询指定游戏下所有进行中的回合 $activeRounds = $game->rounds() ->whereRaw('DATE_ADD(start_time, INTERVAL duration MINUTE) > ?', [now()]) ->get(); // 如果要查询存在进行中回合的游戏实例 $activeGame = Game::whereHas('rounds', function ($query) { $query->whereRaw('DATE_ADD(start_time, INTERVAL duration MINUTE) > ?', [now()]); })->find($gameId);
PostgreSQL 环境
PostgreSQL的时间计算语法不同,用make_interval处理分钟偏移:
$activeRounds = $game->rounds() ->whereRaw("start_time + make_interval(mins => duration) > ?", [now()]) ->get();
高版本Laravel优雅写法(Laravel 8+ 适配对应数据库后可用)
可以用DB::raw包裹字段运算表达式,不用写全量原生where:
$activeRounds = $game->rounds() ->where(now(), '<', DB::raw('DATE_ADD(start_time, INTERVAL duration MINUTE)')) ->get();
避坑提示
- 不要尝试在PHP层提前计算固定时间阈值,每个回合的duration值独立,固定阈值无法适配所有回合记录
- 所有传入SQL的动态值(比如当前时间)用参数绑定方式传入,不要直接拼接到SQL字符串中,避免SQL注入风险
- 已经拿到$game实例的情况下,直接通过
rounds()关联对象追加约束查询,比嵌套whereHas的写法性能更好。
内容的提问来源于stack exchange,提问作者Emile Caron
相关产品推荐
相关产品推荐

