如何简化PHP/Laravel中冗长的考勤数据判断逻辑?
优化方案:Laravel周度存储考勤系统报表生成效率提升
问题背景
数据库采用周度存储设计,以每周一为记录日期,通过MonAM/MonPM等字段存储每日出勤状态。生成跨周或周中报表时,原代码依赖大量嵌套if/switch判断字段,导致代码冗余、执行效率低下,现提供不修改数据库结构的优化方案。
核心优化方向
1. 预构建日期与数据库字段的映射关系
提前定义日期到考勤字段的映射规则,避免每次判断周几再匹配字段。编写辅助函数快速获取指定日期对应的AM/PM字段名:
/** * 根据日期获取对应的考勤字段(如TueAM、TuePM) */ function getAttendanceFieldNames(DateTimeInterface $date): array { $dayAbbr = $date->format('D'); // 返回Mon/Tue/.../Sun return ["{$dayAbbr}AM", "{$dayAbbr}PM"]; }
2. 批量查询替代循环单查
原代码循环每个学生单独查询考勤记录,改为一次性查询所有目标学生的记录并按student_id分组,大幅减少数据库请求次数:
// 先统一计算报表覆盖的周范围 $startDate = DateTimeImmutable::createFromFormat('d/m/y', $request->start_date); $endDate = DateTimeImmutable::createFromFormat('d/m/y', $request->end_date); $weekStart = $startDate->modify('previous monday')->format('Y-m-d'); if ($startDate->format('D') === 'Mon') { $weekStart = $startDate->format('Y-m-d'); } // 批量查询所有学生的考勤记录并分组 $studentsArr = array_merge($student_arr1, $student_arr2); $attendancesGrouped = SchoolAttendanceData::whereIn('student_id', $studentsArr) ->where('school_id', $school_id) ->whereBetween('date', [$weekStart, $endDate->format('Y-m-d')]) ->orderBy('date') ->get() ->groupBy('student_id');
3. 统一处理单周/跨周报表逻辑
不再区分单周或多周场景,直接遍历报表的完整日期范围,对每个日期匹配对应的周考勤记录:
// 生成报表覆盖的所有日期数组 $dateRange = new DatePeriod( $startDate, new DateInterval('P1D'), $endDate->add(new DateInterval('P1D')) // 包含结束日期 ); // 遍历每个学生处理考勤数据 foreach ($studentsArr as $studentId) { $studentAttendances = $attendancesGrouped->get($studentId, collect()); $attendanceCodes = []; foreach ($dateRange as $currentDate) { // 获取当前日期所在周的周一(即考勤记录的date字段值) $recordDate = $currentDate->modify('previous monday')->format('Y-m-d'); if ($currentDate->format('D') === 'Mon') { $recordDate = $currentDate->format('Y-m-d'); } // 找到对应的周考勤记录 $weeklyRecord = $studentAttendances->firstWhere('date', $recordDate); if (!$weeklyRecord) { $attendanceCodes[$currentDate->format('Y-m-d')] = ['AM' => 'N', 'PM' => 'N']; continue; } // 获取当前日期对应的考勤字段值 [$amField, $pmField] = getAttendanceFieldNames($currentDate); $attendanceCodes[$currentDate->format('Y-m-d')] = [ 'AM' => $weeklyRecord->{$amField} ?? 'N', 'PM' => $weeklyRecord->{$pmField} ?? 'N' ]; } // 后续处理$attendanceCodes生成报表... }
4. 移除冗余嵌套判断
原代码中针对单条/多条记录的分支判断可完全移除,通过集合的firstWhere方法直接定位对应周的记录,逻辑更简洁。
优化效果
- 数据库查询次数从
N次(N为学生数)减少为1次,大幅降低IO开销; - 移除所有嵌套
if/switch判断,代码可读性与维护性提升; - 统一的日期处理逻辑,适配所有报表时段(周中、跨周、整周)。
内容的提问来源于stack exchange,提问作者Greenbird
相关产品推荐
相关产品推荐

