You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.20 13:13:09