feat: Gestion des classes scolaires
Permet aux administrateurs de créer, modifier et supprimer des classes pour organiser les élèves par niveau. L'archivage soft-delete préserve l'historique tout en masquant les classes obsolètes. Inclut la validation des noms (2-50 caractères), les niveaux scolaires du CP à la Terminale, et les contrôles d'accès par rôle.
This commit is contained in:
@@ -0,0 +1,81 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Administration\Infrastructure\Api\Processor;
|
||||
|
||||
use ApiPlatform\Metadata\Operation;
|
||||
use ApiPlatform\State\ProcessorInterface;
|
||||
use App\Administration\Application\Command\ArchiveClass\ArchiveClassCommand;
|
||||
use App\Administration\Application\Command\ArchiveClass\ArchiveClassHandler;
|
||||
use App\Administration\Domain\Exception\ClasseNonSupprimableException;
|
||||
use App\Administration\Domain\Exception\ClasseNotFoundException;
|
||||
use App\Administration\Domain\Model\SchoolClass\ClassId;
|
||||
use App\Administration\Domain\Repository\ClassRepository;
|
||||
use App\Administration\Infrastructure\Api\Resource\ClassResource;
|
||||
use App\Administration\Infrastructure\Security\ClassVoter;
|
||||
|
||||
use function is_string;
|
||||
|
||||
use Override;
|
||||
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
|
||||
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
|
||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
use Symfony\Component\Messenger\MessageBusInterface;
|
||||
use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
|
||||
|
||||
/**
|
||||
* Processor API Platform pour supprimer (archiver) une classe.
|
||||
*
|
||||
* @implements ProcessorInterface<ClassResource, null>
|
||||
*/
|
||||
final readonly class DeleteClassProcessor implements ProcessorInterface
|
||||
{
|
||||
public function __construct(
|
||||
private ArchiveClassHandler $handler,
|
||||
private ClassRepository $classRepository,
|
||||
private MessageBusInterface $eventBus,
|
||||
private AuthorizationCheckerInterface $authorizationChecker,
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ClassResource $data
|
||||
*/
|
||||
#[Override]
|
||||
public function process(mixed $data, Operation $operation, array $uriVariables = [], array $context = []): null
|
||||
{
|
||||
$classId = $uriVariables['id'] ?? null;
|
||||
|
||||
if (!is_string($classId)) {
|
||||
throw new BadRequestHttpException('ID de classe manquant.');
|
||||
}
|
||||
|
||||
// Vérifier les permissions avant toute action
|
||||
$class = $this->classRepository->findById(ClassId::fromString($classId));
|
||||
if ($class === null) {
|
||||
throw new NotFoundHttpException('Classe introuvable.');
|
||||
}
|
||||
|
||||
if (!$this->authorizationChecker->isGranted(ClassVoter::DELETE, $class)) {
|
||||
throw new AccessDeniedHttpException('Vous n\'êtes pas autorisé à supprimer cette classe.');
|
||||
}
|
||||
|
||||
try {
|
||||
$command = new ArchiveClassCommand(classId: $classId);
|
||||
|
||||
$archivedClass = ($this->handler)($command);
|
||||
|
||||
// Dispatch domain events from the archived aggregate
|
||||
foreach ($archivedClass->pullDomainEvents() as $event) {
|
||||
$this->eventBus->dispatch($event);
|
||||
}
|
||||
|
||||
return null;
|
||||
} catch (ClasseNotFoundException) {
|
||||
throw new NotFoundHttpException('Classe introuvable.');
|
||||
} catch (ClasseNonSupprimableException $e) {
|
||||
throw new BadRequestHttpException($e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user