ClassService.php 4.8 KB

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