Записи о посещаемости дублируются в нескольких группах для одного и того же учащегося ⇐ Php
-
Anonymous
Записи о посещаемости дублируются в нескольких группах для одного и того же учащегося
У меня возникла проблема с системой посещаемости в моем приложении Laravel. У меня есть система, в которой ученики могут посещать несколько групп, и в каждой группе есть свой учитель. Однако когда учитель из одной группы отмечает посещаемость учащегося, записи, похоже, дублируются в других группах, куда также зачислен учащийся. Я подозреваю, что проблема может быть связана с тем, как функции createMissingAttendanceRecords, createAttendanceRecord и updateAttendance обрабатывают создание записей посещаемости.
Что я проверил на данный момент:
Проверка существующих записей кажется правильной. Отношения между моделями «Группа», «Студент» и «Посещаемость» определены правильно. Я буду признателен за любые идеи и предложения по устранению и решению этой проблемы. Если потребуется дополнительная информация или фрагменты кода, дайте мне знать.
бэкэнд-код
класс ShowGroupAttendance расширяет компонент { используйте WireToast; общественный $groupId; общедоступные $groupDays, $monthDays, $monthDaysDate, $createAttendanceRecordsFlag = false, $selectedMonth, $isModalOpen = false; общественный $studentId, $date, $value, $studentName, $isReasonable; public bool $isUpdatingMonth = false; публичная функция 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'); // Проверяем, существуют ли записи посещаемости за текущий месяц if (!$this->groupId->attendanceRecordsExistForMonth($currentMonth)) { // Установите флаг, чтобы гарантировать, что записи создаются только один раз во время первоначального рендеринга $this->createAttendanceRecordsFlag = true; // Создаём записи посещаемости для каждого студента $студенты = $this->groupId->студенты; foreach ($students как $student) { $this->createAttendanceRecord($student, $currentMonth); } } еще { $this->createMissingAttendanceRecords($currentMonth); } } частная функция createMissingAttendanceRecords($currentMonth) { // Получаем существующих пользователей группы $existingUsers = $this->groupId->students->pluck('id')->toArray(); // Получаем пользователей, для которых созданы записи посещаемости $usersWithAttendanceRecords = $this->groupId->attendance() ->where('group_id', $this->groupId->id) // Рассматриваем только записи текущей группы ->whereYear('date', Carbon::parse($currentMonth)->year) ->whereMonth('дата', Carbon::parse($currentMonth)->месяц) -> pluck('student_id') ->ToArray(); // Находим новых пользователей, которым нужны записи посещаемости $newUsers = array_diff($existingUsers, $usersWithAttendanceRecords); // Создаём записи посещаемости для новых пользователей foreach ($newUsers как $userId) { $студент = Студент::найти($userId); $this->createAttendanceRecord($student, $currentMonth); } тост() ->success('', 'Посещаемость добавляется с новыми студентами для ' . дата('F')) -> продолжительность (2500) -> нажать(); } частная функция createAttendanceRecord($student, $currentMonth) { $groupDays = $this->groupDays; // Проходим каждый день недели, указанный для группы foreach ($groupDays как $dayOfWeek) { $firstDayOfMonth = Carbon::createFromDate($currentMonth, 1); $difference = ($dayOfWeek - $firstDayOfMonth->dayOfWeek + 7) % 7; $date = $firstDayOfMonth->copy()->addDays($difference); // Проверяем, находится ли дата в текущем месяце while ($date->month == Carbon::parse($currentMonth)->month) { $existingRecord = $this->groupId->attendance() ->where('student_id', $student->id) ->where('date', $date->format('Г-м-д')) ->where('group_id', $this->groupId->id) -> существует (); если (!$existingRecord) { $this->groupId->attendance()->create([ 'student_id' => $student->id, 'group_id' => $this->groupId->id, 'date' => $date->copy(), // Копируем дату, чтобы не изменять оригинал 'is_present' => ложь, 'is_default_status' => правда, ]); } // Переход к следующему вхождению того же дня недели $date->addWeek(); } } тост() ->success('', 'Посещаемость создана для ' . дата('F')) -> продолжительность (2500) -> нажать(); } публичная функция updateAttendance($studentId, $date, $value, $studentName, $isReasonable) { $attendanceDate = Carbon::parse($date); // Проверяем, совпадает ли дата посещения с сегодняшней датой if (!$attendanceDate->isToday()) { тост() ->warning('Посещаемость может быть обновлена только на сегодня', $studentName) -> продолжительность (1000) -> нажать(); возвращаться; } // Проверяем, совпадает ли дата посещения с текущей датой $attendance = $this->groupId->attendance() ->where('student_id', $studentId) ->где('дата', $дата) ->первый(); если ($ посещаемость) { переключатель ($значение) { случай «истина»: $attendance->обновление([ 'is_present' => правда, 'is_default_status' => ложь, 'is_reasonable' => ноль, ]); тост() ->success('отмечен как в классе', $studentName) -> продолжительность (1000) -> нажать(); перерыв; случай «ложь»: если ($isReasonable){ $attendance->обновление([ 'is_present' => ложь, 'is_default_status' => ложь, 'is_reasonable' => правда, ]); } еще { $attendance->обновление([ 'is_present' => ложь, 'is_default_status' => ложь, 'is_reasonable' => ложь, ]); } тост() ->danger('отмечено как не в классе', $studentName) -> продолжительность (1000) -> нажать(); перерыв; по умолчанию: прерывание (403); } } } частная функция генерироватьMonthDays() { $now = Carbon::parse($this->selectedMonth); $this->monthDays = []; $this->monthDaysDate = []; // Вычисляем первый день месяца $firstDayOfMonth = Carbon::create($now->year, $now->month, 1); // Проходим каждый день месяца for ($day = 1; $day daysInMonth; $day++) { $currentDate = Carbon::create($now->year, $now->month, $day); // Проверяем, находится ли день в указанном массивеdays_in_week if (in_array($currentDate->dayOfWeek, $this->groupDays)) { $this->monthDays[] = $currentDate->format('j M'); $this->monthDaysDate[] = $currentDate->format('Г-м-д'); } } } публичная функция nextMonth() { $this->selectedMonth = Carbon::parse($this->selectedMonth)->addMonth()->format('Y-m'); $this->generateMonthDays(); } публичная функция previousMonth() { $this->selectedMonth = Carbon::parse($this->selectedMonth)->subMonth()->format('Y-m'); $this->generateMonthDays(); } публичная функция openModal($studentId, $date, $value, $studentName) { $this->studentId = $studentId; $this->date = $date; $это->значение = $значение; $this->studentName = $studentName; $this->isModalOpen = true; } публичная функция saveModal() { $this->updateAttendance($this->studentId, $this->date, $this->value, $this->studentName, $this->isReasonable); $this-> closeModal(); } публичная функция closeModal() { $this->isModalOpen = ложь; $this->studentId = ''; $this->date = ''; $this->value = ''; $this->studentName = ''; $this->isReasonable = ''; } заполнитель публичной функции() { return view('livewire.placeholder.placeholder'); } публичная функция рендеринга() { $студенты = $this->groupId->студенты; $showMonth = Carbon::parse($this->selectedMonth)->format('F Y'); return view('livewire.teacher.attendance.show-group-attendance', [ 'студенты' => $студенты, 'monthDays' => $this->monthDays, 'шоуМесяц' => $шоумонс, ]); } } Модель группы:
Группа классов расширяет модель { используйте HasFactory; защищенный $fillable = [ 'title', 'subject_id', 'teacher_id', 'group_fee', 'days_in_week', 'start_time', 'end_time', 'color', 'start_date', 'end_date', ]; учитель общественных функций() { вернуть $this->belongsToMany(Teacher::class); } студенты общественных мероприятий() { вернуть $this->belongsToMany(Student::class); } субъекты публичной функции() { return $this->belongsTo(Subject::class, 'subject_id'); } публичная функция TotalStudents() { вернуть $this->students->count(); } публичная функция StudentPayments() { return $this->hasMany(StudentPayment::class, 'group_id'); } публичная функция getStudentsBySubjects() { $subjectIds = $this->subjects()->pluck('id')->toArray(); return Student::whereHas('subjects', function ($query) use ($subjectIds) { $query->whereIn('subject_id', $subjectIds); })->получить(); } посещаемость общественных мероприятий() { вернуть $this->hasMany(Attendance::class); } посещаемость публичной функцииRecordsExistForMonth($month) { вернуть $this->attendance() ->whereYear('дата', Carbon::parse($месяц)->год) ->whereMonth('дата', Carbon::parse($месяц)->месяц) -> существует (); } } Модель посещаемости:
Класс «Посещаемость» расширяет модель { используйте HasFactory; защищенный $fillable = [ «group_id», «student_id», «дата», «is_present», «is_default_status», «is_reasonable» ]; группа общедоступных функций() { вернуть $this->belongsTo(Group::class); } студент публичной функции() { вернуть $this->belongsTo(Student::class); } } Студенческая модель:
класс Студент расширяет модель { используйте HasFactory; защищенный $fillable = [ 'имя', 'номер_телефона', 'вторичный_номер_телефона', 'курс', ]; субъекты публичной функции() { вернуть $this->belongsToMany(Subject::class); } группы общедоступных функций() { вернуть $this->belongsToMany(Group::class); } сборы за общественные функции() { вернуть $this->hasMany(StudentFee::class); } посещаемость общественных мероприятий() { вернуть $this->hasMany(Attendance::class); } }
У меня возникла проблема с системой посещаемости в моем приложении Laravel. У меня есть система, в которой ученики могут посещать несколько групп, и в каждой группе есть свой учитель. Однако когда учитель из одной группы отмечает посещаемость учащегося, записи, похоже, дублируются в других группах, куда также зачислен учащийся. Я подозреваю, что проблема может быть связана с тем, как функции createMissingAttendanceRecords, createAttendanceRecord и updateAttendance обрабатывают создание записей посещаемости.
Что я проверил на данный момент:
Проверка существующих записей кажется правильной. Отношения между моделями «Группа», «Студент» и «Посещаемость» определены правильно. Я буду признателен за любые идеи и предложения по устранению и решению этой проблемы. Если потребуется дополнительная информация или фрагменты кода, дайте мне знать.
бэкэнд-код
класс ShowGroupAttendance расширяет компонент { используйте WireToast; общественный $groupId; общедоступные $groupDays, $monthDays, $monthDaysDate, $createAttendanceRecordsFlag = false, $selectedMonth, $isModalOpen = false; общественный $studentId, $date, $value, $studentName, $isReasonable; public bool $isUpdatingMonth = false; публичная функция 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'); // Проверяем, существуют ли записи посещаемости за текущий месяц if (!$this->groupId->attendanceRecordsExistForMonth($currentMonth)) { // Установите флаг, чтобы гарантировать, что записи создаются только один раз во время первоначального рендеринга $this->createAttendanceRecordsFlag = true; // Создаём записи посещаемости для каждого студента $студенты = $this->groupId->студенты; foreach ($students как $student) { $this->createAttendanceRecord($student, $currentMonth); } } еще { $this->createMissingAttendanceRecords($currentMonth); } } частная функция createMissingAttendanceRecords($currentMonth) { // Получаем существующих пользователей группы $existingUsers = $this->groupId->students->pluck('id')->toArray(); // Получаем пользователей, для которых созданы записи посещаемости $usersWithAttendanceRecords = $this->groupId->attendance() ->where('group_id', $this->groupId->id) // Рассматриваем только записи текущей группы ->whereYear('date', Carbon::parse($currentMonth)->year) ->whereMonth('дата', Carbon::parse($currentMonth)->месяц) -> pluck('student_id') ->ToArray(); // Находим новых пользователей, которым нужны записи посещаемости $newUsers = array_diff($existingUsers, $usersWithAttendanceRecords); // Создаём записи посещаемости для новых пользователей foreach ($newUsers как $userId) { $студент = Студент::найти($userId); $this->createAttendanceRecord($student, $currentMonth); } тост() ->success('', 'Посещаемость добавляется с новыми студентами для ' . дата('F')) -> продолжительность (2500) -> нажать(); } частная функция createAttendanceRecord($student, $currentMonth) { $groupDays = $this->groupDays; // Проходим каждый день недели, указанный для группы foreach ($groupDays как $dayOfWeek) { $firstDayOfMonth = Carbon::createFromDate($currentMonth, 1); $difference = ($dayOfWeek - $firstDayOfMonth->dayOfWeek + 7) % 7; $date = $firstDayOfMonth->copy()->addDays($difference); // Проверяем, находится ли дата в текущем месяце while ($date->month == Carbon::parse($currentMonth)->month) { $existingRecord = $this->groupId->attendance() ->where('student_id', $student->id) ->where('date', $date->format('Г-м-д')) ->where('group_id', $this->groupId->id) -> существует (); если (!$existingRecord) { $this->groupId->attendance()->create([ 'student_id' => $student->id, 'group_id' => $this->groupId->id, 'date' => $date->copy(), // Копируем дату, чтобы не изменять оригинал 'is_present' => ложь, 'is_default_status' => правда, ]); } // Переход к следующему вхождению того же дня недели $date->addWeek(); } } тост() ->success('', 'Посещаемость создана для ' . дата('F')) -> продолжительность (2500) -> нажать(); } публичная функция updateAttendance($studentId, $date, $value, $studentName, $isReasonable) { $attendanceDate = Carbon::parse($date); // Проверяем, совпадает ли дата посещения с сегодняшней датой if (!$attendanceDate->isToday()) { тост() ->warning('Посещаемость может быть обновлена только на сегодня', $studentName) -> продолжительность (1000) -> нажать(); возвращаться; } // Проверяем, совпадает ли дата посещения с текущей датой $attendance = $this->groupId->attendance() ->where('student_id', $studentId) ->где('дата', $дата) ->первый(); если ($ посещаемость) { переключатель ($значение) { случай «истина»: $attendance->обновление([ 'is_present' => правда, 'is_default_status' => ложь, 'is_reasonable' => ноль, ]); тост() ->success('отмечен как в классе', $studentName) -> продолжительность (1000) -> нажать(); перерыв; случай «ложь»: если ($isReasonable){ $attendance->обновление([ 'is_present' => ложь, 'is_default_status' => ложь, 'is_reasonable' => правда, ]); } еще { $attendance->обновление([ 'is_present' => ложь, 'is_default_status' => ложь, 'is_reasonable' => ложь, ]); } тост() ->danger('отмечено как не в классе', $studentName) -> продолжительность (1000) -> нажать(); перерыв; по умолчанию: прерывание (403); } } } частная функция генерироватьMonthDays() { $now = Carbon::parse($this->selectedMonth); $this->monthDays = []; $this->monthDaysDate = []; // Вычисляем первый день месяца $firstDayOfMonth = Carbon::create($now->year, $now->month, 1); // Проходим каждый день месяца for ($day = 1; $day daysInMonth; $day++) { $currentDate = Carbon::create($now->year, $now->month, $day); // Проверяем, находится ли день в указанном массивеdays_in_week if (in_array($currentDate->dayOfWeek, $this->groupDays)) { $this->monthDays[] = $currentDate->format('j M'); $this->monthDaysDate[] = $currentDate->format('Г-м-д'); } } } публичная функция nextMonth() { $this->selectedMonth = Carbon::parse($this->selectedMonth)->addMonth()->format('Y-m'); $this->generateMonthDays(); } публичная функция previousMonth() { $this->selectedMonth = Carbon::parse($this->selectedMonth)->subMonth()->format('Y-m'); $this->generateMonthDays(); } публичная функция openModal($studentId, $date, $value, $studentName) { $this->studentId = $studentId; $this->date = $date; $это->значение = $значение; $this->studentName = $studentName; $this->isModalOpen = true; } публичная функция saveModal() { $this->updateAttendance($this->studentId, $this->date, $this->value, $this->studentName, $this->isReasonable); $this-> closeModal(); } публичная функция closeModal() { $this->isModalOpen = ложь; $this->studentId = ''; $this->date = ''; $this->value = ''; $this->studentName = ''; $this->isReasonable = ''; } заполнитель публичной функции() { return view('livewire.placeholder.placeholder'); } публичная функция рендеринга() { $студенты = $this->groupId->студенты; $showMonth = Carbon::parse($this->selectedMonth)->format('F Y'); return view('livewire.teacher.attendance.show-group-attendance', [ 'студенты' => $студенты, 'monthDays' => $this->monthDays, 'шоуМесяц' => $шоумонс, ]); } } Модель группы:
Группа классов расширяет модель { используйте HasFactory; защищенный $fillable = [ 'title', 'subject_id', 'teacher_id', 'group_fee', 'days_in_week', 'start_time', 'end_time', 'color', 'start_date', 'end_date', ]; учитель общественных функций() { вернуть $this->belongsToMany(Teacher::class); } студенты общественных мероприятий() { вернуть $this->belongsToMany(Student::class); } субъекты публичной функции() { return $this->belongsTo(Subject::class, 'subject_id'); } публичная функция TotalStudents() { вернуть $this->students->count(); } публичная функция StudentPayments() { return $this->hasMany(StudentPayment::class, 'group_id'); } публичная функция getStudentsBySubjects() { $subjectIds = $this->subjects()->pluck('id')->toArray(); return Student::whereHas('subjects', function ($query) use ($subjectIds) { $query->whereIn('subject_id', $subjectIds); })->получить(); } посещаемость общественных мероприятий() { вернуть $this->hasMany(Attendance::class); } посещаемость публичной функцииRecordsExistForMonth($month) { вернуть $this->attendance() ->whereYear('дата', Carbon::parse($месяц)->год) ->whereMonth('дата', Carbon::parse($месяц)->месяц) -> существует (); } } Модель посещаемости:
Класс «Посещаемость» расширяет модель { используйте HasFactory; защищенный $fillable = [ «group_id», «student_id», «дата», «is_present», «is_default_status», «is_reasonable» ]; группа общедоступных функций() { вернуть $this->belongsTo(Group::class); } студент публичной функции() { вернуть $this->belongsTo(Student::class); } } Студенческая модель:
класс Студент расширяет модель { используйте HasFactory; защищенный $fillable = [ 'имя', 'номер_телефона', 'вторичный_номер_телефона', 'курс', ]; субъекты публичной функции() { вернуть $this->belongsToMany(Subject::class); } группы общедоступных функций() { вернуть $this->belongsToMany(Group::class); } сборы за общественные функции() { вернуть $this->hasMany(StudentFee::class); } посещаемость общественных мероприятий() { вернуть $this->hasMany(Attendance::class); } }
Мобильная версия