ClassService.php 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149
  1. <?php
  2. namespace App\Services;
  3. use App\Models\ClassAttendance;
  4. use App\Models\SchoolClass;
  5. use App\Models\Student;
  6. use Illuminate\Database\Eloquent\Collection;
  7. use Illuminate\Support\Facades\DB;
  8. class ClassService
  9. {
  10. // aulas da unidade ativa, com nome do pacote e contadores
  11. // total de alunos do pacote e quantos foram marcados presentes
  12. public function getAllByUnit(int $unitId): Collection
  13. {
  14. return SchoolClass::with('packageUnit')
  15. ->withCount([
  16. 'attendances as present_count' => fn ($q) => $q->where('in_class', true),
  17. ])
  18. ->where('unit_id', $unitId)
  19. ->orderBy('date_time_start')
  20. ->get()
  21. ->each(function (SchoolClass $class) {
  22. $class->students_count = $this->activeStudentsQuery($class->class_package_unit_id, $class->unit_id)->count();
  23. });
  24. }
  25. public function findByIdForUnit(int $id, int $unitId): SchoolClass
  26. {
  27. return SchoolClass::with('packageUnit')
  28. ->where('unit_id', $unitId)
  29. ->findOrFail($id);
  30. }
  31. public function create(array $data): SchoolClass
  32. {
  33. return SchoolClass::create($data)->load('packageUnit');
  34. }
  35. public function update(int $id, int $unitId, array $data): SchoolClass
  36. {
  37. $model = $this->findByIdForUnit($id, $unitId);
  38. $model->update($data);
  39. return $model->fresh('packageUnit');
  40. }
  41. public function delete(int $id, int $unitId): bool
  42. {
  43. $model = $this->findByIdForUnit($id, $unitId);
  44. return (bool) $model->delete();
  45. }
  46. // lista os alunos com contrato ativo no pacote vinculado a aula
  47. // ja mesclando a presenca registrada se houver para aquela aula
  48. public function getStudentsForClass(int $classId, int $unitId): array
  49. {
  50. $class = $this->findByIdForUnit($classId, $unitId);
  51. $students = $this->activeStudentsQuery($class->class_package_unit_id, $class->unit_id)
  52. ->orderBy('students.name')
  53. ->get();
  54. // presencas ja registradas nesta aula, indexadas por aluno
  55. $attendances = ClassAttendance::where('class_id', $classId)
  56. ->get()
  57. ->keyBy('student_id');
  58. // total de aulas presenciadas por aluno em qualquer aula
  59. $attendedCounts = ClassAttendance::select('student_id', DB::raw('COUNT(*) as total'))
  60. ->whereIn('student_id', $students->pluck('id'))
  61. ->where('in_class', true)
  62. ->groupBy('student_id')
  63. ->pluck('total', 'student_id');
  64. return $students->map(function (Student $student) use ($attendances, $attendedCounts) {
  65. $attendance = $attendances->get($student->id);
  66. return [
  67. 'student_id' => $student->id,
  68. 'name' => $student->name,
  69. 'photo_url' => $student->photo_url,
  70. 'classes_count' => (int) ($attendedCounts[$student->id] ?? 0),
  71. 'in_class' => (bool) ($attendance?->in_class ?? false),
  72. 'justified' => (bool) ($attendance?->justified ?? false),
  73. 'notes' => $attendance?->notes,
  74. ];
  75. })->all();
  76. }
  77. // salva a presenca da aula em lote upsert por aluno
  78. // items: student_id, in_class, justified e notes
  79. public function saveAttendance(int $classId, int $unitId, array $items): void
  80. {
  81. $class = $this->findByIdForUnit($classId, $unitId);
  82. $allowedStudentIds = $this->activeStudentsQuery($class->class_package_unit_id, $unitId)
  83. ->whereIn('students.id', collect($items)->pluck('student_id'))
  84. ->pluck('students.id');
  85. abort_unless($allowedStudentIds->count() === count($items), 422, 'A lista contém alunos que não pertencem a esta aula.');
  86. DB::transaction(function () use ($classId, $items) {
  87. foreach ($items as $item) {
  88. $inClass = (bool) ($item['in_class'] ?? false);
  89. // justificativa so vale para faltas, presenca nunca fica justificada
  90. $justified = $inClass ? false : (bool) ($item['justified'] ?? false);
  91. ClassAttendance::updateOrCreate(
  92. [
  93. 'class_id' => $classId,
  94. 'student_id' => $item['student_id'],
  95. ],
  96. [
  97. 'in_class' => $inClass,
  98. 'justified' => $justified,
  99. 'notes' => $justified ? ($item['notes'] ?? null) : null,
  100. ],
  101. );
  102. }
  103. });
  104. }
  105. // query base de alunos com contrato ativo no pacote informado e na unidade
  106. private function activeStudentsQuery(?int $classPackageUnitId, int $unitId)
  107. {
  108. return Student::query()
  109. ->whereHas('contracts', function ($q) use ($classPackageUnitId, $unitId) {
  110. $q->latestVersion()
  111. ->where('status', 'active')
  112. ->where('unit_id', $unitId);
  113. if ($classPackageUnitId) {
  114. $q->where('class_package_unit_id', $classPackageUnitId);
  115. }
  116. });
  117. }
  118. }