*/ final readonly class StudentGradeCollectionProvider implements ProviderInterface { public function __construct( private Connection $connection, private TenantContext $tenantContext, private Security $security, ) { } /** @return list */ #[Override] public function provide(Operation $operation, array $uriVariables = [], array $context = []): array { if (!$this->tenantContext->hasTenant()) { throw new UnauthorizedHttpException('Bearer', 'Tenant non défini.'); } $user = $this->security->getUser(); if (!$user instanceof SecurityUser) { throw new UnauthorizedHttpException('Bearer', 'Authentification requise.'); } if (!in_array(Role::ELEVE->value, $user->getRoles(), true)) { throw new AccessDeniedHttpException('Accès réservé aux élèves.'); } $tenantId = (string) $this->tenantContext->getCurrentTenantId(); $studentId = $user->userId(); /** @var string|null $subjectId */ $subjectId = $uriVariables['subjectId'] ?? null; if (is_string($subjectId) && $subjectId === '') { $subjectId = null; } $subjectFilter = $subjectId !== null ? 'AND s.id = :subject_id' : ''; $rows = $this->connection->fetchAllAssociative( "SELECT g.id AS grade_id, g.value, g.status AS grade_status, g.appreciation, e.id AS evaluation_id, e.title AS evaluation_title, e.evaluation_date, e.grade_scale, e.coefficient, e.grades_published_at, s.id AS subject_id, s.name AS subject_name, es.average AS class_average, es.min_grade AS class_min, es.max_grade AS class_max FROM grades g JOIN evaluations e ON g.evaluation_id = e.id JOIN subjects s ON e.subject_id = s.id LEFT JOIN evaluation_statistics es ON es.evaluation_id = e.id WHERE g.student_id = :student_id AND g.tenant_id = :tenant_id AND e.grades_published_at IS NOT NULL AND e.status != :deleted_status {$subjectFilter} ORDER BY e.evaluation_date DESC, e.created_at DESC", $subjectId !== null ? ['student_id' => $studentId, 'tenant_id' => $tenantId, 'deleted_status' => 'deleted', 'subject_id' => $subjectId] : ['student_id' => $studentId, 'tenant_id' => $tenantId, 'deleted_status' => 'deleted'], ); return array_map(self::hydrateResource(...), $rows); } /** @param array $row */ private static function hydrateResource(array $row): StudentGradeResource { $resource = new StudentGradeResource(); /** @var string $gradeId */ $gradeId = $row['grade_id']; $resource->id = $gradeId; /** @var string $evaluationId */ $evaluationId = $row['evaluation_id']; $resource->evaluationId = $evaluationId; /** @var string $evaluationTitle */ $evaluationTitle = $row['evaluation_title']; $resource->evaluationTitle = $evaluationTitle; /** @var string $evaluationDate */ $evaluationDate = $row['evaluation_date']; $resource->evaluationDate = $evaluationDate; /** @var string|int $gradeScale */ $gradeScale = $row['grade_scale']; $resource->gradeScale = (int) $gradeScale; /** @var string|float $coefficient */ $coefficient = $row['coefficient']; $resource->coefficient = (float) $coefficient; /** @var string $subjectIdVal */ $subjectIdVal = $row['subject_id']; $resource->subjectId = $subjectIdVal; /** @var string|null $subjectName */ $subjectName = $row['subject_name']; $resource->subjectName = $subjectName; /** @var string|float|null $value */ $value = $row['value']; $resource->value = $value !== null ? (float) $value : null; /** @var string $gradeStatus */ $gradeStatus = $row['grade_status']; $resource->status = $gradeStatus; /** @var string|null $appreciation */ $appreciation = $row['appreciation']; $resource->appreciation = $appreciation; /** @var string|null $publishedAt */ $publishedAt = $row['grades_published_at']; $resource->publishedAt = $publishedAt; /** @var string|float|null $classAverage */ $classAverage = $row['class_average']; $resource->classAverage = $classAverage !== null ? (float) $classAverage : null; /** @var string|float|null $classMin */ $classMin = $row['class_min']; $resource->classMin = $classMin !== null ? (float) $classMin : null; /** @var string|float|null $classMax */ $classMax = $row['class_max']; $resource->classMax = $classMax !== null ? (float) $classMax : null; return $resource; } }