如何用PHP实现月度DTR(每日考勤记录)?数据库考勤记录匹配问题
实现PHP月度DTR(每日考勤记录)功能及日期匹配方案
嘿,我来帮你搞定PHP实现月度DTR的事儿,尤其是你提到的「跳过缺勤日期只展示有记录日期」这个需求——我之前做考勤系统的时候经常碰到类似的场景,给你梳理个清晰的方案:
第一步:先搞定数据库结构
首先得有个靠谱的考勤记录表,我通常会这么设计(用MySQL举例):
CREATE TABLE attendance ( id INT AUTO_INCREMENT PRIMARY KEY, employee_id INT NOT NULL, -- 关联员工表的ID attendance_date DATE NOT NULL, -- 考勤日期,用DATE类型避免格式问题 check_in TIME NULL, -- 上班打卡时间,缺勤的话可以设为NULL check_out TIME NULL, -- 下班打卡时间 status ENUM('present', 'absent', 'late', 'leave') NOT NULL DEFAULT 'absent', -- 考勤状态 created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP );
小提示: 把attendance_date设为DATE类型,后续匹配日期会少很多格式兼容的麻烦。
第二步:从数据库提取指定员工的月度考勤记录
接下来用PHP查询目标员工当月的所有考勤记录,并且把结果整理成「日期为键」的数组,方便后续快速匹配:
// 假设你已经用PDO或mysqli连接了数据库,这里用PDO示例 $pdo = new PDO('mysql:host=localhost;dbname=your_db', 'username', 'password'); $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); // 要查询的员工ID和月份(可以改成用户输入的参数) $employeeId = 123; $targetYear = date('Y'); $targetMonth = date('m'); // 查询该员工当月的所有考勤记录,按日期排序 $stmt = $pdo->prepare(" SELECT attendance_date, check_in, check_out, status FROM attendance WHERE employee_id = ? AND YEAR(attendance_date) = ? AND MONTH(attendance_date) = ? ORDER BY attendance_date ASC "); $stmt->execute([$employeeId, $targetYear, $targetMonth]); $attendanceData = $stmt->fetchAll(PDO::FETCH_ASSOC); // 整理成以日期为键的数组,方便后续快速查找 $attendanceRecords = []; foreach ($attendanceData as $record) { $attendanceRecords[$record['attendance_date']] = $record; }
第三步:生成DTR表单并跳过缺勤日期
这里分两种常见场景,你可以按需选择:
场景1:只展示有考勤记录的日期(直接跳过缺勤日)
这种最简单,直接遍历查询到的记录就行,自然就跳过了没有记录的缺勤日期:
echo '<div class="dtr-container">'; echo '<h2>月度考勤记录(员工ID:' . $employeeId . ' | ' . $targetYear . '年' . $targetMonth . '月)</h2>'; echo '<table border="1" cellpadding="8">'; echo '<tr><th>日期</th><th>上班时间</th><th>下班时间</th><th>状态</th></tr>'; foreach ($attendanceData as $record) { echo '<tr>'; echo '<td>' . $record['attendance_date'] . '</td>'; echo '<td>' . ($record['check_in'] ?? '无') . '</td>'; echo '<td>' . ($record['check_out'] ?? '无') . '</td>'; echo '<td>' . ucfirst($record['status']) . '</td>'; echo '</tr>'; } echo '</table></div>';
场景2:遍历当月所有日期,遇到缺勤日直接跳过不显示
如果需要严格按月度日期顺序,但只展示有记录的行,就先生成当月所有日期,再逐一匹配:
// 生成当月所有日期的数组 $firstDayOfMonth = "$targetYear-$targetMonth-01"; $lastDayOfMonth = date('Y-m-t', strtotime($firstDayOfMonth)); $currentDate = $firstDayOfMonth; $monthDates = []; while ($currentDate <= $lastDayOfMonth) { $monthDates[] = $currentDate; $currentDate = date('Y-m-d', strtotime($currentDate . ' +1 day')); } // 生成DTR表格 echo '<div class="dtr-container">'; echo '<h2>月度考勤记录(员工ID:' . $employeeId . ' | ' . $targetYear . '年' . $targetMonth . '月)</h2>'; echo '<table border="1" cellpadding="8">'; echo '<tr><th>日期</th><th>上班时间</th><th>下班时间</th><th>状态</th></tr>'; foreach ($monthDates as $date) { // 只有当该日期有考勤记录时才输出行 if (isset($attendanceRecords[$date])) { $record = $attendanceRecords[$date]; echo '<tr>'; echo '<td>' . $date . '</td>'; echo '<td>' . ($record['check_in'] ?? '无') . '</td>'; echo '<td>' . ($record['check_out'] ?? '无') . '</td>'; echo '<td>' . ucfirst($record['status']) . '</td>'; echo '</tr>'; } // 没有记录就跳过,不输出任何内容 } echo '</table></div>';
一些额外的优化建议
- 可以添加员工选择器和月份选择器,让用户能切换不同员工和月份生成DTR;
- 如果需要区分「未打卡」和「请假」,可以在
status字段里细化枚举值; - 可以给DTR表格加上样式,让它更美观,比如用CSS隐藏边框、添加 hover 效果。
内容的提问来源于stack exchange,提问作者Decent
相关产品推荐
相关产品推荐

