Laravel模型关联场景下,如何查询指定时段全可用用户?
查询指定时段内完全可用的用户
问题说明
现有User与Availability模型,关联关系为User hasMany Availabilities,对应user_availability表数据如下:
id | user_id | date 123 | 12345 | 2024-02-01 124 | 12345 | 2024-02-02 125 | 12345 | 2024-02-03 126 | 45678 | 2024-02-01 127 | 45678 | 2024-02-03
需要筛选出指定时段内每一天都有可用记录的用户。比如查询2024-02-01至2024-02-03时,仅返回user_id=12345,user_id=45678因缺少2024-02-02的记录需排除。
原控制器查询代码:
$query->whereHas('availabilities', function ($query) use ($periodStartDate, $periodEndDate) { $query->fullyAvailable($periodStartDate, $periodEndDate); });
原Availability模型的scopeFullyAvailable函数逻辑错误,会返回所有在时段内有任意可用记录的用户,不符合需求。
修正方案
方案一:修改Scope函数
核心逻辑是统计用户在指定时段内的可用日期数量,必须等于时段的总天数。修改后的Scope函数如下:
public function scopeFullyAvailable($query, $startDate, $endDate) { $start = Carbon::parse($startDate); $end = Carbon::parse($endDate); $totalDays = $start->diffInDays($end) + 1; // 计算时段总天数,加1是因为包含首尾两天 return $query->whereDate('date', '>=', $start) ->whereDate('date', '<=', $end) // 在这里添加你的额外筛选条件,比如->where('status', 'available') ->groupBy('user_id') ->havingRaw('COUNT(DISTINCT date) = ?', [$totalDays]); }
方案二:简化控制器查询(更高效)
直接利用whereHas的第三个参数指定关联记录的数量要求,无需复杂Scope:
$start = Carbon::parse($periodStartDate); $end = Carbon::parse($periodEndDate); $totalDays = $start->diffInDays($end) + 1; $query->whereHas('availabilities', function ($query) use ($start, $end) { $query->whereDate('date', '>=', $start) ->whereDate('date', '<=', $end); // 这里添加你的额外筛选条件 }, '=', $totalDays);
原代码错误原因
原Scope中的循环whereDate会生成多个AND条件,但外层的orWhere导致逻辑变成“满足额外筛选 或者 存在任意一个指定日期的记录”,这和“每一天都要有记录”的需求完全相反,自然会返回所有在时段内有记录的用户。
内容的提问来源于stack exchange,提问作者eggsloveoats
相关产品推荐
相关产品推荐

