Lorsqu'un admin créait un élève de moins de 15 ans avec une date de naissance, le compte ne pouvait pas être activé car le consentement parental RGPD n'avait jamais été enregistré — aucun mécanisme ne le permettait dans le parcours admin. Ajout d'une case « Consentement parental obtenu » dans le formulaire de création d'élève, affichée conditionnellement quand la date de naissance indique un âge < 15 ans. L'admin confirme que l'établissement a recueilli le consentement, qui est alors enregistré côté backend lors de la création du compte.
117 lines
4.9 KiB
PHP
117 lines
4.9 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Administration\Infrastructure\Api\Processor;
|
|
|
|
use ApiPlatform\Metadata\Operation;
|
|
use ApiPlatform\State\ProcessorInterface;
|
|
use App\Administration\Application\Command\CreateStudent\CreateStudentCommand;
|
|
use App\Administration\Application\Command\CreateStudent\CreateStudentHandler;
|
|
use App\Administration\Domain\Exception\ClasseNotFoundException;
|
|
use App\Administration\Domain\Exception\EmailDejaUtiliseeException;
|
|
use App\Administration\Domain\Model\SchoolClass\ClassId;
|
|
use App\Administration\Domain\Repository\ClassRepository;
|
|
use App\Administration\Infrastructure\Api\Resource\StudentResource;
|
|
use App\Administration\Infrastructure\Security\SecurityUser;
|
|
use App\Administration\Infrastructure\Security\StudentVoter;
|
|
use App\Administration\Infrastructure\Service\CurrentAcademicYearResolver;
|
|
use App\Shared\Infrastructure\Tenant\TenantContext;
|
|
use Override;
|
|
use Symfony\Bundle\SecurityBundle\Security;
|
|
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
|
|
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
|
|
use Symfony\Component\HttpKernel\Exception\ConflictHttpException;
|
|
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
|
use Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException;
|
|
use Symfony\Component\Messenger\MessageBusInterface;
|
|
use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
|
|
|
|
/**
|
|
* @implements ProcessorInterface<StudentResource, StudentResource>
|
|
*/
|
|
final readonly class CreateStudentProcessor implements ProcessorInterface
|
|
{
|
|
public function __construct(
|
|
private CreateStudentHandler $handler,
|
|
private ClassRepository $classRepository,
|
|
private TenantContext $tenantContext,
|
|
private MessageBusInterface $eventBus,
|
|
private AuthorizationCheckerInterface $authorizationChecker,
|
|
private CurrentAcademicYearResolver $academicYearResolver,
|
|
private Security $security,
|
|
) {
|
|
}
|
|
|
|
/**
|
|
* @param StudentResource $data
|
|
*/
|
|
#[Override]
|
|
public function process(mixed $data, Operation $operation, array $uriVariables = [], array $context = []): StudentResource
|
|
{
|
|
if (!$this->authorizationChecker->isGranted(StudentVoter::CREATE)) {
|
|
throw new AccessDeniedHttpException('Vous n\'êtes pas autorisé à créer un élève.');
|
|
}
|
|
|
|
if (!$this->tenantContext->hasTenant()) {
|
|
throw new UnauthorizedHttpException('Bearer', 'Tenant non défini.');
|
|
}
|
|
|
|
$tenantId = (string) $this->tenantContext->getCurrentTenantId();
|
|
$tenantConfig = $this->tenantContext->getCurrentTenantConfig();
|
|
$academicYearId = $this->academicYearResolver->resolve('current')
|
|
?? throw new BadRequestHttpException('Impossible de résoudre l\'année scolaire courante.');
|
|
|
|
try {
|
|
$adminUserId = null;
|
|
$securityUser = $this->security->getUser();
|
|
|
|
if ($securityUser instanceof SecurityUser) {
|
|
$adminUserId = $securityUser->userId();
|
|
}
|
|
|
|
$command = new CreateStudentCommand(
|
|
tenantId: $tenantId,
|
|
schoolName: $tenantConfig->subdomain,
|
|
firstName: $data->firstName ?? '',
|
|
lastName: $data->lastName ?? '',
|
|
classId: $data->classId ?? '',
|
|
academicYearId: $academicYearId,
|
|
email: $data->email,
|
|
dateNaissance: $data->dateNaissance,
|
|
studentNumber: $data->studentNumber,
|
|
parentalConsent: $data->parentalConsent,
|
|
consentRecordedBy: $adminUserId,
|
|
);
|
|
|
|
$user = ($this->handler)($command);
|
|
|
|
// Dispatch domain events
|
|
foreach ($user->pullDomainEvents() as $event) {
|
|
$this->eventBus->dispatch($event);
|
|
}
|
|
|
|
// Build the response from the created user and class info
|
|
$class = $this->classRepository->get(ClassId::fromString($data->classId ?? ''));
|
|
|
|
$resource = new StudentResource();
|
|
$resource->id = (string) $user->id;
|
|
$resource->firstName = $user->firstName;
|
|
$resource->lastName = $user->lastName;
|
|
$resource->email = $user->email !== null ? (string) $user->email : null;
|
|
$resource->statut = $user->statut->value;
|
|
$resource->classId = $data->classId;
|
|
$resource->className = (string) $class->name;
|
|
$resource->classLevel = $class->level?->value;
|
|
$resource->studentNumber = $user->studentNumber;
|
|
$resource->dateNaissance = $user->dateNaissance?->format('Y-m-d');
|
|
|
|
return $resource;
|
|
} catch (EmailDejaUtiliseeException $e) {
|
|
throw new ConflictHttpException($e->getMessage());
|
|
} catch (ClasseNotFoundException $e) {
|
|
throw new NotFoundHttpException($e->getMessage());
|
|
}
|
|
}
|
|
}
|