false])] private bool $isSystem = false; /** @var Collection */ // Choix deliberé de fetch: 'EAGER' (durcissement, pas oubli de perf) : // - Evite un lazy-load silencieux pendant un refresh de token JWT ou une // serialisation hors contexte EntityManager (voir ticket #343, section // 11 risque #1) ou la collection serait inaccessible et provoquerait // une erreur opaque. // - Compromis accepte : surcout SQL volontaire, acceptable a l'echelle // d'un CRM/ERP PME ou un role porte quelques dizaines de permissions. // - Si la volumetrie augmente significativement : revoir vers une // projection cachee (ticket a ouvrir a ce moment-la). #[ORM\ManyToMany(targetEntity: Permission::class, fetch: 'EAGER')] #[ORM\JoinTable(name: 'role_permission')] private Collection $permissions; public function __construct(string $code, string $label, bool $isSystem = false, ?string $description = null) { $this->code = $code; $this->label = $label; $this->isSystem = $isSystem; $this->description = $description; $this->permissions = new ArrayCollection(); } public function getId(): ?int { return $this->id; } public function getCode(): string { return $this->code; } public function getLabel(): string { return $this->label; } public function getDescription(): ?string { return $this->description; } public function isSystem(): bool { return $this->isSystem; } /** @return Collection */ public function getPermissions(): Collection { return $this->permissions; } /** * Met a jour le libelle affichable du role. Le code reste immuable pour * garantir la stabilite des references cote fixtures et migrations. */ public function setLabel(string $label): static { $this->label = $label; return $this; } /** * Met a jour la description libre du role (champ documentaire). */ public function setDescription(?string $description): static { $this->description = $description; return $this; } /** * Ajoute une permission au role. Idempotent : ajouter deux fois la meme * permission n'entraine pas de doublon dans la collection. */ public function addPermission(Permission $permission): static { if (!$this->permissions->contains($permission)) { $this->permissions->add($permission); } return $this; } /** * Retire une permission du role. Idempotent : retirer une permission * absente est un no-op silencieux. */ public function removePermission(Permission $permission): static { $this->permissions->removeElement($permission); return $this; } /** * Garde domaine : refuse la suppression d'un role marque comme systeme. * La traduction HTTP (403) est faite au niveau application / API Platform. */ public function ensureDeletable(): void { if ($this->isSystem) { throw SystemRoleDeletionException::forRole($this); } } }