*/ final class TeacherStatisticsExportProvider implements ProviderInterface { use HandleTrait; public function __construct( MessageBusInterface $queryBus, private readonly TenantContext $tenantContext, private readonly Security $security, private readonly StatisticsExporter $exporter, ) { $this->messageBus = $queryBus; } #[Override] public function provide(Operation $operation, array $uriVariables = [], array $context = []): Response { 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::PROF->value, $user->getRoles(), true)) { throw new AccessDeniedHttpException('Accès réservé aux enseignants.'); } /** @var array $filters */ $filters = $context['filters'] ?? []; /** @var string|null $classId */ $classId = $filters['classId'] ?? null; /** @var string|null $subjectId */ $subjectId = $filters['subjectId'] ?? null; /** @var string|null $className */ $className = $filters['className'] ?? 'Classe'; /** @var string|null $subjectName */ $subjectName = $filters['subjectName'] ?? 'Matière'; if (!is_string($classId) || $classId === '') { throw new BadRequestHttpException('Le paramètre classId est requis.'); } if (!is_string($subjectId) || $subjectId === '') { throw new BadRequestHttpException('Le paramètre subjectId est requis.'); } /** @var ClassStatisticsDetailDto $dto */ $dto = $this->handle(new GetClassStatisticsDetailQuery( teacherId: $user->userId(), classId: $classId, subjectId: $subjectId, tenantId: (string) $this->tenantContext->getCurrentTenantId(), )); $csv = $this->exporter->exportClassToCsv($dto, (string) $className, (string) $subjectName); // Sanitize filename: remove characters dangerous in Content-Disposition $safeFilename = str_replace(['"', "\r", "\n", '/', '\\'], '', sprintf('statistiques-%s-%s.csv', $className, $subjectName)); return new Response( content: $csv, headers: [ 'Content-Type' => 'text/csv; charset=UTF-8', 'Content-Disposition' => sprintf('attachment; filename="%s"', $safeFilename), ], ); } }