Laravel中筛选工作日并排除节假日统计有效出勤天数
Laravel统计用户有效出勤天数解决方案
核心需求实现思路
直接通过数据库查询筛选+统计,比集合处理更高效,避免内存占用问题。以下以MySQL为例,适配你的需求:
方案一:使用Query Builder直接统计
$startDate = '2023-12-25'; $endDate = '2023-12-31'; $activeAttendance = DB::table('presences') // 关联用户表获取用户名 ->join('users', 'presences.user_id', '=', 'users.id') // 限定日期范围 ->whereBetween('presences.presence_date', [$startDate, $endDate]) // 筛选周一至周五(MySQL的WEEKDAY:0=周一,4=周五) ->whereRaw('WEEKDAY(presences.presence_date) BETWEEN 0 AND 4') // 排除指定日期范围内的节假日 ->whereNotIn('presences.presence_date', function($subQuery) use ($startDate, $endDate) { $subQuery->select('holiday_date') ->from('holidays') ->whereBetween('holiday_date', [$startDate, $endDate]); }) // 统计字段(去重避免同一天多条记录重复计数) ->select( 'presences.user_id', 'users.name as user_name', DB::raw('COUNT(DISTINCT presences.presence_date) as active_day') ) // 按用户分组 ->groupBy('presences.user_id', 'users.name') ->get();
方案二:集合处理(适合需额外业务逻辑的场景)
如果需要先获取所有出勤记录再做复杂处理,可使用Laravel集合:
use Carbon\Carbon; $startDate = '2023-12-25'; $endDate = '2023-12-31'; // 获取指定日期范围内的所有出勤记录(关联用户信息) $presences = \App\Models\Presence::with('user') ->whereBetween('presence_date', [$startDate, $endDate]) ->get(); // 获取需要排除的节假日日期(转成字符串格式统一对比) $excludedHolidays = \App\Models\Holiday::whereBetween('holiday_date', [$startDate, $endDate]) ->pluck('holiday_date') ->map(fn($date) => $date->format('Y-m-d')) ->toArray(); // 筛选有效出勤并统计 $activeAttendance = $presences ->filter(function($presence) use ($excludedHolidays) { $presenceDate = Carbon::parse($presence->presence_date); // 排除周末 if ($presenceDate->isWeekend()) { return false; } // 排除节假日 if (in_array($presenceDate->format('Y-m-d'), $excludedHolidays)) { return false; } return true; }) ->groupBy('user_id') ->map(function($group) { return [ 'user_id' => $group->first()->user_id, 'user_name' => $group->first()->user->name, 'active_day' => $group->count() ]; }) ->values();
关键注意事项
- 数据库日期匹配:确保
presences.presence_date和holidays.holiday_date格式一致(如均为Y-m-d),若字段是datetime类型,需用DATE(presence_date)进行日期比较 - 数据库函数适配:不同数据库的星期函数不同:
- PostgreSQL:用
EXTRACT(DOW FROM presence_date) BETWEEN 1 AND 5(1=周一,5=周五) - SQL Server:用
DATEPART(WEEKDAY, presence_date) BETWEEN 2 AND 6(2=周一,6=周五)
- PostgreSQL:用
- 去重统计:若用户同一天有多条出勤记录,需用
COUNT(DISTINCT presences.presence_date)避免重复计数
示例输出
[ {"user_id": 1, "user_name": "张三", "active_day": 3}, {"user_id": 2, "user_name": "李四", "active_day": 4} ]
内容的提问来源于stack exchange,提问作者Toni Rhubanaga
相关产品推荐
相关产品推荐

