['Default', 'create']], name: 'create_subject', ), new Patch( uriTemplate: '/subjects/{id}', provider: SubjectItemProvider::class, processor: UpdateSubjectProcessor::class, validationContext: ['groups' => ['Default', 'update']], name: 'update_subject', ), new Delete( uriTemplate: '/subjects/{id}', provider: SubjectItemProvider::class, processor: DeleteSubjectProcessor::class, name: 'delete_subject', ), ], )] final class SubjectResource { #[ApiProperty(identifier: true)] public ?string $id = null; #[Assert\NotBlank(message: 'Le nom de la matière est requis.', groups: ['create'])] #[Assert\Length( min: 2, max: 100, minMessage: 'Le nom de la matière doit contenir au moins {{ limit }} caractères.', maxMessage: 'Le nom de la matière ne peut pas dépasser {{ limit }} caractères.', )] public ?string $name = null; #[Assert\NotBlank(message: 'Le code de la matière est requis.', groups: ['create'])] #[Assert\Regex( pattern: '/^[A-Za-z0-9]{2,10}$/', message: 'Le code doit contenir entre 2 et 10 caractères alphanumériques.', )] public ?string $code = null; #[Assert\Regex( pattern: '/^#[0-9A-Fa-f]{6}$/', message: 'La couleur doit être au format hexadécimal #RRGGBB.', )] public ?string $color = null; public ?string $description = null; public ?string $status = null; public ?DateTimeImmutable $createdAt = null; public ?DateTimeImmutable $updatedAt = null; /** * Statistiques : nombre d'enseignants associés à cette matière. * Disponible uniquement dans GetCollection. */ #[ApiProperty(readable: true, writable: false)] public ?int $teacherCount = null; /** * Statistiques : nombre de classes associées à cette matière. * Disponible uniquement dans GetCollection. */ #[ApiProperty(readable: true, writable: false)] public ?int $classCount = null; /** * Statistiques : nombre d'évaluations créées pour cette matière. */ #[ApiProperty(readable: true, writable: false)] public ?int $evaluationCount = null; /** * Statistiques : nombre de notes saisies pour cette matière. */ #[ApiProperty(readable: true, writable: false)] public ?int $gradeCount = null; /** * Permet de supprimer explicitement la couleur lors d'un PATCH. * Si true, la couleur sera mise à null même si color n'est pas fourni. */ #[ApiProperty(readable: false)] public ?bool $clearColor = null; /** * Permet de supprimer explicitement la description lors d'un PATCH. * Si true, la description sera mise à null même si description n'est pas fourni. */ #[ApiProperty(readable: false)] public ?bool $clearDescription = null; /** * Indique si la matière a des notes associées (pour avertissement avant suppression). */ #[ApiProperty(readable: true, writable: false)] public ?bool $hasGrades = null; /** * Crée un SubjectResource à partir du domain model. */ public static function fromDomain(Subject $subject): self { $resource = new self(); $resource->id = (string) $subject->id; $resource->name = (string) $subject->name; $resource->code = (string) $subject->code; $resource->color = $subject->color !== null ? (string) $subject->color : null; $resource->description = $subject->description; $resource->status = $subject->status->value; $resource->createdAt = $subject->createdAt; $resource->updatedAt = $subject->updatedAt; return $resource; } /** * Crée un SubjectResource à partir d'un DTO de query. */ public static function fromDto(SubjectDto $dto): self { $resource = new self(); $resource->id = $dto->id; $resource->name = $dto->name; $resource->code = $dto->code; $resource->color = $dto->color; $resource->description = $dto->description; $resource->status = $dto->status; $resource->createdAt = $dto->createdAt; $resource->updatedAt = $dto->updatedAt; $resource->teacherCount = $dto->teacherCount; $resource->classCount = $dto->classCount; $resource->evaluationCount = $dto->evaluationCount; $resource->gradeCount = $dto->gradeCount; $resource->hasGrades = $dto->hasGrades(); return $resource; } }