Laravel考勤系统中同一学生考勤记录跨多组重复的问题排查与解决求助
Laravel考勤系统中同一学生考勤记录跨多组重复的问题排查与解决求助
各位好,我在Laravel项目里搭建的考勤系统遇到了一个奇怪的问题:学生可以同时加入多个小组,每个小组对应不同的老师,但当某一个小组的老师给学生标记考勤后,这个学生在其他已加入小组的考勤记录居然会出现重复的情况。我初步怀疑问题出在createMissingAttendanceRecords、createAttendanceRecord和updateAttendance这几个负责创建/更新考勤记录的函数里,但自己排查了一阵没找到关键问题。
我已经做了这些检查:
- 现有考勤记录的存在性判断逻辑看起来是正确的
- Group、Student和Attendance三个模型之间的关联关系已经正确定义
希望有经验的朋友能给我一些排查方向或者解决建议,如果需要查看更多细节或者其他代码片段,我随时提供。
后端Livewire组件代码(ShowGroupAttendance)
class ShowGroupAttendance extends Component { use WireToast; public $groupId; public $groupDays, $monthDays, $monthDaysDate, $createAttendanceRecordsFlag = false, $selectedMonth, $isModalOpen = false; public $studentId, $date, $value, $studentName, $isReasonable; public bool $isUpdatingMonth = false; public function mount($id) { $this->groupId = Group::find($id); $this->groupDays = json_decode($this->groupId->days_in_week, true); $this->generateMonthDays(); $this->selectedMonth = Carbon::now()->format('Y-m'); $currentMonth = Carbon::now()->format('Y-m'); // Check if attendance records exist for the current month if (!$this->groupId->attendanceRecordsExistForMonth($currentMonth)) { // Set a flag to ensure that records are only created once during the initial render $this->createAttendanceRecordsFlag = true; // Create attendance records for each student $students = $this->groupId->students; foreach ($students as $student) { $this->createAttendanceRecord($student, $currentMonth); } } else { $this->createMissingAttendanceRecords($currentMonth); } } private function createMissingAttendanceRecords($currentMonth) { // Get the existing users for the group $existingUsers = $this->groupId->students->pluck('id')->toArray(); // Get the users for whom attendance records have been created $usersWithAttendanceRecords = $this->groupId->attendance() ->where('group_id', $this->groupId->id) // Consider only records for the current group ->whereYear('date', Carbon::parse($currentMonth)->year) ->whereMonth('date', Carbon::parse($currentMonth)->month) ->pluck('student_id') ->toArray(); // Find the new users that need attendance records $newUsers = array_diff($existingUsers, $usersWithAttendanceRecords); // Create attendance records for new users foreach ($newUsers as $userId) { $student = Student::find($userId); $this->createAttendanceRecord($student, $currentMonth); } toast() ->success('', 'Attendance is added with new students for ' . date('F')) ->duration(2500) ->push(); } private function createAttendanceRecord($student, $currentMonth) { $groupDays = $this->groupDays; // Loop through each day of the week specified for the group foreach ($groupDays as $dayOfWeek) { $firstDayOfMonth = Carbon::createFromDate($currentMonth, 1); $difference = ($dayOfWeek - $firstDayOfMonth->dayOfWeek + 7) % 7; $date = $firstDayOfMonth->copy()->addDays($difference); // Check if the date is within the current month while ($date->month == Carbon::parse($currentMonth)->month) { $existingRecord = $this->groupId->attendance() ->where('student_id', $student->id) ->where('date', $date->format('Y-m-d')) ->where('group_id', $this->groupId->id) ->exists(); if (!$existingRecord) { $this->groupId->attendance()->create([ 'student_id' => $student->id, 'group_id' => $this->groupId->id, 'date' => $date->copy(), // Copy the date to avoid modifying the original 'is_present' => false, 'is_default_status' => true, ]); } // Move to the next occurrence of the same day of the week $date->addWeek(); } } toast() ->success('', 'Attendance is created for ' . date('F')) ->duration(2500) ->push(); } public function updateAttendance($studentId, $date, $value, $studentName, $isReasonable) { $attendanceDate = Carbon::parse($date); // Check if the attendance date is the same as today's date if (!$attendanceDate->isToday()) { toast() ->warning('Attendance can only be updated for today', $studentName) ->duration(1000) ->push(); return; } // Check if the attendance date is the same as the current date $attendance = $this->groupId->attendance() ->where('student_id', $studentId) ->where('date', $date) ->first(); if ($attendance) { switch ($value) { case 'true': $attendance->update([ 'is_present' => true, 'is_default_status' => false, 'is_reasonable' => null, ]); toast() ->success('is marked as in the class', $studentName) ->duration(1000) ->push(); break; case 'false': if ($isReasonable){ $attendance->update([ 'is_present' => false, 'is_default_status' => false, 'is_reasonable' => true, ]); } else { $attendance->update([ 'is_present' => false, 'is_default_status' => false, 'is_reasonable' => false, ]); } toast() ->danger('is marked as not in the class', $studentName) ->duration(1000) ->push(); break; default: abort(403); } } } private function generateMonthDays() { $now = Carbon::parse($this->selectedMonth); $this->monthDays = []; $this->monthDaysDate = []; // Calculate the first day of the month $firstDayOfMonth = Carbon::create($now->year, $now->month, 1); // Loop through each day of the month for ($day = 1; $day <= $firstDayOfMonth->daysInMonth; $day++) { $currentDate = Carbon::create($now->year, $now->month, $day); // Check if the day is in the specified days_in_week array if (in_array($currentDate->dayOfWeek, $this->groupDays)) { $this->monthDays[] = $currentDate->format('j M'); $this->monthDaysDate[] = $currentDate->format('Y-m-d'); } } } public function nextMonth() { $this->selectedMonth = Carbon::parse($this->selectedMonth)->addMonth()->format('Y-m'); $this->generateMonthDays(); } public function previousMonth() { $this->selectedMonth = Carbon::parse($this->selectedMonth)->subMonth()->format('Y-m'); $this->generateMonthDays(); } public function openModal($studentId, $date, $value, $studentName) { $this->studentId = $studentId; $this->date = $date; $this->value = $value; $this->studentName = $studentName; $this->isModalOpen = true; } public function saveModal() { $this->updateAttendance($this->studentId, $this->date, $this->value, $this->studentName, $this->isReasonable); $this->closeModal(); } public function closeModal() { $this->isModalOpen = false; $this->studentId = ''; $this->date = ''; $this->value = ''; $this->studentName = ''; $this->isReasonable = ''; } public function placeholder() { return view('livewire.placeholder.placeholder'); } public function render() { $students = $this->groupId->students; $showMonth = Carbon::parse($this->selectedMonth)->format('F Y'); return view('livewire.teacher.attendance.show-group-attendance', [ 'students' => $students, 'monthDays' => $this->monthDays, 'showMonth' => $showMonth, ]); } }
Group模型代码
class Group extends Model { use HasFactory; protected $fillable = [ 'title', 'subject_id', 'teacher_id', 'group_fee', 'days_in_week', 'start_time', 'end_time', 'color', 'start_date', 'end_date', ]; public function teacher() { return $this->belongsToMany(Teacher::class); } public function students() { return $this->belongsToMany(Student::class); } public function subjects() { return $this->belongsTo(Subject::class, 'subject_id'); } public function totalStudents() { return $this->students->count(); } public function studentPayments() { return $this->hasMany(StudentPayment::class, 'group_id'); } public function getStudentsBySubjects() { $subjectIds = $this->subjects()->pluck('id')->toArray(); return Student::whereHas('subjects', function ($query) use ($subjectIds) { $query->whereIn('subject_id', $subjectIds); })->get(); } public function attendance() { return $this->hasMany(Attendance::class); } public function attendanceRecordsExistForMonth($month) { return $this->attendance() ->whereYear('date', Carbon::parse($month)->year) ->whereMonth('date', Carbon::parse($month)->month) ->exists(); } }
Attendance模型代码
class Attendance extends Model { use HasFactory; protected $fillable = [ 'group_id', 'student_id', 'date', 'is_present', 'is_default_status', 'is_reasonable' ]; public function group() { return $this->belongsTo(Group::class); } public function student() { return $this->belongsTo(Student::class); } }
Student模型代码
class Student extends Model { use HasFactory; protected $fillable = [ 'name', 'phone_number', 'secondary_phone_number', 'course', ]; public function subjects() { return $this->belongsToMany(Subject::class); } public function groups() { return $this->belongsToMany(Group::class); } public function fees() { return $this->hasMany(StudentFee::class); } public function attendance() { return $this->hasMany(Attendance::class); } }
备注:内容来源于stack exchange,提问作者Raxmatullo Erkinov
相关产品推荐
相关产品推荐

