| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147 |
- <?php
- namespace App\Services;
- use App\Models\SchoolClass;
- use App\Models\ClassAttendance;
- use App\Models\Student;
- use Illuminate\Database\Eloquent\Collection;
- use Illuminate\Support\Facades\DB;
- class ClassService
- {
- /**
- * Aulas da unidade ativa, com nome do pacote e contadores
- * (total de alunos do pacote e quantos foram marcados presentes).
- */
- public function getAllByUnit(int $unitId): Collection
- {
- return SchoolClass::with('packageUnit')
- ->withCount([
- 'attendances as present_count' => fn ($q) => $q->where('in_class', true),
- ])
- ->where('unit_id', $unitId)
- ->orderBy('date_time_start')
- ->get()
- ->each(function (SchoolClass $class) {
- $class->students_count = $this->activeStudentsQuery($class->class_package_unit_id, $class->unit_id)->count();
- });
- }
- public function findByIdForUnit(int $id, int $unitId): SchoolClass
- {
- return SchoolClass::with('packageUnit')
- ->where('unit_id', $unitId)
- ->findOrFail($id);
- }
- public function create(array $data): SchoolClass
- {
- return SchoolClass::create($data)->load('packageUnit');
- }
- public function update(int $id, int $unitId, array $data): SchoolClass
- {
- $model = $this->findByIdForUnit($id, $unitId);
- $model->update($data);
- return $model->fresh('packageUnit');
- }
- public function delete(int $id, int $unitId): bool
- {
- $model = $this->findByIdForUnit($id, $unitId);
- return (bool) $model->delete();
- }
- /**
- * Lista os alunos com contrato ATIVO no pacote vinculado à aula,
- * já mesclando a presença registrada (se houver) para aquela aula.
- */
- public function getStudentsForClass(int $classId, int $unitId): array
- {
- $class = $this->findByIdForUnit($classId, $unitId);
- $students = $this->activeStudentsQuery($class->class_package_unit_id, $class->unit_id)
- ->orderBy('students.name')
- ->get();
- // Presenças já registradas nesta aula, indexadas por aluno
- $attendances = ClassAttendance::where('class_id', $classId)
- ->get()
- ->keyBy('student_id');
- // Total de aulas presenciadas por aluno (em qualquer aula)
- $attendedCounts = ClassAttendance::select('student_id', DB::raw('COUNT(*) as total'))
- ->whereIn('student_id', $students->pluck('id'))
- ->where('in_class', true)
- ->groupBy('student_id')
- ->pluck('total', 'student_id');
- return $students->map(function (Student $student) use ($attendances, $attendedCounts) {
- $attendance = $attendances->get($student->id);
- return [
- 'student_id' => $student->id,
- 'name' => $student->name,
- 'photo_url' => $student->photo_url,
- 'classes_count' => (int) ($attendedCounts[$student->id] ?? 0),
- 'in_class' => (bool) ($attendance?->in_class ?? false),
- 'justified' => (bool) ($attendance?->justified ?? false),
- 'notes' => $attendance?->notes,
- ];
- })->all();
- }
- /**
- * Salva a presença da aula em lote (upsert por aluno).
- *
- * @param array<int, array{student_id:int, in_class:bool, justified?:bool, notes?:string|null}> $items
- */
- public function saveAttendance(int $classId, int $unitId, array $items): void
- {
- $class = $this->findByIdForUnit($classId, $unitId);
- $allowedStudentIds = $this->activeStudentsQuery($class->class_package_unit_id, $unitId)
- ->whereIn('students.id', collect($items)->pluck('student_id'))
- ->pluck('students.id');
- abort_unless($allowedStudentIds->count() === count($items), 422, 'A lista contém alunos que não pertencem a esta aula.');
- DB::transaction(function () use ($classId, $items) {
- foreach ($items as $item) {
- $inClass = (bool) ($item['in_class'] ?? false);
- // Justificativa só vale para faltas; presença nunca fica justificada.
- $justified = $inClass ? false : (bool) ($item['justified'] ?? false);
- ClassAttendance::updateOrCreate(
- [
- 'class_id' => $classId,
- 'student_id' => $item['student_id'],
- ],
- [
- 'in_class' => $inClass,
- 'justified' => $justified,
- 'notes' => $justified ? ($item['notes'] ?? null) : null,
- ],
- );
- }
- });
- }
- /**
- * Query base: alunos com contrato ativo no pacote informado (e na unidade).
- */
- private function activeStudentsQuery(?int $classPackageUnitId, int $unitId)
- {
- return Student::query()
- ->whereHas('contracts', function ($q) use ($classPackageUnitId, $unitId) {
- $q->where('status', 'active')
- ->where('unit_id', $unitId);
- if ($classPackageUnitId) {
- $q->where('class_package_unit_id', $classPackageUnitId);
- }
- });
- }
- }
|