From d7677db88538bec0f94cff7e3fd0a896b23d2025 Mon Sep 17 00:00:00 2001 From: Charles Date: Fri, 25 Jul 2025 12:03:54 +0200 Subject: [PATCH] refactor --- .gitignore | 4 + src/Service/UserOrganizationService.php | 228 ++++++++++++++++-------- 2 files changed, 162 insertions(+), 70 deletions(-) diff --git a/.gitignore b/.gitignore index 4daae38..347a90c 100644 --- a/.gitignore +++ b/.gitignore @@ -23,3 +23,7 @@ /public/assets/ /assets/vendor/ ###< symfony/asset-mapper ### + +###> IntelliJ IDEA ### +.idea/ +*.iml diff --git a/src/Service/UserOrganizationService.php b/src/Service/UserOrganizationService.php index 7ac60f9..9e2bb5d 100644 --- a/src/Service/UserOrganizationService.php +++ b/src/Service/UserOrganizationService.php @@ -9,19 +9,28 @@ use App\Entity\User; use App\Entity\UsersOrganizations; use Doctrine\ORM\EntityManagerInterface; +/** + * Service pour la gestion des organisations d'utilisateurs. + * Fournit des méthodes pour récupérer, modifier et désactiver les rôles et applications d'un utilisateur dans une organisation. + */ readonly class UserOrganizationService { + /** + * Constructeur du service UserOrganizationService. + * + * @param EntityManagerInterface $entityManager Le gestionnaire d'entités Doctrine + */ public function __construct(private readonly EntityManagerInterface $entityManager) { } /** - * Returns all organizations the given user belongs to, - * including the unique roles and apps the user has in each organization. + * Récupère toutes les organisations auxquelles appartient l'utilisateur donné, + * incluant les rôles et applications uniques de l'utilisateur dans chaque organisation. * - * @param User $user The user whose organizations are being fetched - * @param int|null $organizationsId Optional organization ID to filter by - * @return array + * @param User $user L'utilisateur concerné + * @param int|null $organizationsId ID optionnel pour filtrer par organisation + * @return array */ public function getUserOrganizations(User $user, int $organizationsId = null): array { @@ -33,29 +42,60 @@ readonly class UserOrganizationService foreach ($userOrganizations as $uo) { $orgId = $uo->getOrganization()->getId(); - // If $organizationsId is provided, skip other organizations + // Si $organizationsId est fourni, ignorer les autres organisations if ($organizationsId !== null && $orgId !== $organizationsId) { continue; } - // Initialize the organization entry if it doesn't exist + // Initialiser l'entrée de l'organisation si elle n'existe pas $organizations[$orgId] = $organizations[$orgId] ?? $this->createEmptyOrganizationBucket($uo); $organizations[$orgId]['uoId'] = $uo->getId(); - // Aggregate roles & apps + // Agréger les rôles et applications $this->addRole($organizations[$orgId]['roles'], $uo->getRole()); $this->addApps($organizations[$orgId]['apps'], $uo->getApps()); } + // Ordonner les rôles : Super Admin, Admin, puis les autres + foreach ($organizations as &$org) { + $org['roles'] = $this->sortRoles($org['roles']); + } + unset($org); + $this->normalizeAppsIndexes($organizations); return array_values($organizations); } /** - * Build the initial array structure for a fresh organization entry. + * Trie les rôles pour que Super Admin et Admin soient en premier, puis les autres. * - * @param UsersOrganizations $link + * @param Roles[] $roles + * @return Roles[] + */ + private function sortRoles(array $roles): array + { + usort($roles, function ($a, $b) { + $priority = [ + 'SUPER_ADMIN' => 0, + 'ADMIN' => 1 + ]; + $aName = strtoupper($a->getName()); + $bName = strtoupper($b->getName()); + $aPriority = $priority[$aName] ?? 2; + $bPriority = $priority[$bName] ?? 2; + if ($aPriority === $bPriority) { + return strcmp($aName, $bName); + } + return $aPriority <=> $bPriority; + }); + return $roles; + } + + /** + * Initialise la structure de données pour une organisation. + * + * @param UsersOrganizations $link Lien utilisateur-organisation * @return array{organization:object, roles:Roles[], apps:array} */ private function createEmptyOrganizationBucket(UsersOrganizations $link): array @@ -68,10 +108,11 @@ readonly class UserOrganizationService } /** - * Add a Role entity to the roles array only if it is not already present (by ID). + * Ajoute un rôle à la liste si non déjà présent (par ID). * - * @param Roles[] &$roles - * @param Roles|null $role + * @param Roles[] &$roles Liste des rôles + * @param Roles|null $role Rôle à ajouter + * @return void */ private function addRole(array &$roles, ?Roles $role): void { @@ -87,10 +128,11 @@ readonly class UserOrganizationService } /** - * Merge one or many apps into the apps map, keeping only one entry per id. + * Fusionne une ou plusieurs applications dans le tableau associatif, une entrée par ID. * - * @param array &$apps - * @param iterable $appsToAdd Collection returned by $userOrganizations->getApps() + * @param array &$apps Tableau des applications + * @param iterable $appsToAdd Applications à ajouter + * @return void */ private function addApps(array &$apps, iterable $appsToAdd): void { @@ -100,10 +142,10 @@ readonly class UserOrganizationService } /** - * Convert apps from associative maps (keyed by id) to plain indexed arrays, - * so the final output is clean JSON-able. + * Normalise le tableau des applications pour le rendre indexé (JSON-friendly). * - * @param array &$organizations + * @param array &$organizations Tableau des organisations + * @return void */ private function normalizeAppsIndexes(array &$organizations): void { @@ -113,57 +155,106 @@ readonly class UserOrganizationService } /** - * Set user organizations with roles, ensuring USER role is always present + * Définit les rôles d'un utilisateur dans une organisation, en s'assurant que le rôle USER est toujours présent. + * Désactive tous les rôles si USER n'est pas sélectionné. * - * @param User $user - * @param Organizations $organization - * @param array $selectedRoles Array of role IDs to set for the user in the organization + * @param User $user L'utilisateur + * @param Organizations $organization L'organisation + * @param array $selectedRoles Tableau des IDs de rôles sélectionnés + * @return void + * @throws \RuntimeException Si le rôle USER n'est pas trouvé */ - public function setUserOrganizations(User $user, Organizations $organization, array $selectedRoles): void { $repo = $this->entityManager->getRepository(UsersOrganizations::class); $roleRepo = $this->entityManager->getRepository(Roles::class); - - // Get the USER role entity $userRole = $roleRepo->findOneBy(['name' => 'USER']); if (!$userRole) { throw new \RuntimeException('USER role not found'); } - - // If USER role is not selected, deactivate all roles and return if (!in_array($userRole->getId(), $selectedRoles)) { $this->deactivateAllUserRoles($user, $organization); return; } - - // 1. Get all current UsersOrganizations for this user/org $currentUserOrgs = $repo->findBy([ 'users' => $user, 'organization' => $organization ]); + $currentRolesMap = $this->mapUserOrgRoles($currentUserOrgs); + $selectedRoles = $this->ensureUserRolePresent($selectedRoles, $userRole->getId()); + $this->addOrUpdateRoles($selectedRoles, $currentRolesMap, $roleRepo, $user, $organization); + $this->deactivateUnselectedRoles($currentRolesMap); + $this->entityManager->flush(); + } - // 2. Build a map: roleId => UsersOrganizations entity - $currentRolesMap = []; - foreach ($currentUserOrgs as $uo) { - $currentRolesMap[$uo->getRole()->getId()] = $uo; + /** + * Met à jour les applications associées à l'utilisateur dans une organisation. + * + * @param User $user L'utilisateur + * @param Organizations $organization L'organisation + * @param array $selectedApps Tableau des IDs d'applications sélectionnées + * @return void + */ + public function setUserOrganizationsApps(User $user, Organizations $organization, array $selectedApps): void + { + $roleUser = $this->entityManager->getRepository(Roles::class)->findOneBy(['name' => 'USER']); + $uoEntity = $this->entityManager + ->getRepository(UsersOrganizations::class) + ->findOneBy(['users' => $user, 'organization' => $organization, 'role' => $roleUser]); + if (!$uoEntity) { + return; } + $this->removeUnselectedApps($uoEntity, $selectedApps); + $this->addSelectedApps($uoEntity, $selectedApps); + $this->entityManager->persist($uoEntity); + $this->entityManager->flush(); + } - // 3. Check if we need to ensure USER role exists + /** + * Crée une map des rôles actuels de l'utilisateur dans l'organisation. + * @param array $currentUserOrgs + * @return array + */ + private function mapUserOrgRoles(array $currentUserOrgs): array + { + $map = []; + foreach ($currentUserOrgs as $uo) { + $map[$uo->getRole()->getId()] = $uo; + } + return $map; + } + + /** + * S'assure que le rôle USER est présent dans la sélection si nécessaire. + * @param array $selectedRoles + * @param int $userRoleId + * @return array + */ + private function ensureUserRolePresent(array $selectedRoles, int $userRoleId): array + { $hasNonUserRole = false; foreach ($selectedRoles as $roleId) { - if ($roleId !== $userRole->getId()) { + if ($roleId !== $userRoleId) { $hasNonUserRole = true; break; } } - - // 4. If we have non-USER roles, ensure USER role is present - if ($hasNonUserRole && !in_array($userRole->getId(), $selectedRoles)) { - $selectedRoles[] = $userRole->getId(); + if ($hasNonUserRole && !in_array($userRoleId, $selectedRoles)) { + $selectedRoles[] = $userRoleId; } + return $selectedRoles; + } - // 5. Add new roles that are selected but not present + /** + * Ajoute ou réactive les rôles sélectionnés pour l'utilisateur dans l'organisation. + * @param array $selectedRoles + * @param array $currentRolesMap + * @param $roleRepo + * @param User $user + * @param Organizations $organization + */ + private function addOrUpdateRoles(array $selectedRoles, array &$currentRolesMap, $roleRepo, User $user, Organizations $organization): void + { foreach ($selectedRoles as $roleId) { if (!isset($currentRolesMap[$roleId])) { $roleEntity = $roleRepo->find($roleId); @@ -172,66 +263,63 @@ readonly class UserOrganizationService $newUserOrganization->setUsers($user); $newUserOrganization->setRole($roleEntity); $newUserOrganization->setOrganization($organization); - $newUserOrganization->setIsActive(true); // Ensure new roles are active + $newUserOrganization->setIsActive(true); $this->entityManager->persist($newUserOrganization); } } else { - // If role exists but was inactive, reactivate it $currentRolesMap[$roleId]->setIsActive(true); $this->entityManager->persist($currentRolesMap[$roleId]); } - // Remove from map so we know which ones to deactivate later unset($currentRolesMap[$roleId]); } + } - // 6. Remove roles that are present but not selected (deactivate them) + /** + * Désactive les rôles non sélectionnés pour l'utilisateur dans l'organisation. + * @param array $currentRolesMap + */ + private function deactivateUnselectedRoles(array $currentRolesMap): void + { foreach ($currentRolesMap as $uo) { $uo->setIsActive(false); $this->entityManager->persist($uo); } - - $this->entityManager->flush(); } - public function setUserOrganizationsApps(User $user, Organizations $organization, array $selectedApps) + /** + * Retire les applications non sélectionnées de l'utilisateur dans l'organisation. + * @param UsersOrganizations $uoEntity + * @param array $selectedApps + */ + private function removeUnselectedApps(UsersOrganizations $uoEntity, array $selectedApps): void { - $apps = []; - $userOrganizations = $this->entityManager - ->getRepository(UsersOrganizations::class) - ->findAllDistinctOrganizationsByUserId($user->getId()); - foreach ($userOrganizations as $uo) { - $this->addApps($apps, $uo->getApps()); - } - - $roleUser = $this->entityManager->getRepository(Roles::class)->findOneBy(['name' => 'USER']); - $uoEntity = $this->entityManager - ->getRepository(UsersOrganizations::class) - ->findOneBy(['users' => $user, 'organization' => $organization, 'role' => $roleUser]); -// dd($uoEntity->getApps()->toArray()); - // 1. Remove apps that are no longer selected foreach ($uoEntity->getApps()->toArray() as $existingApp) { if (!in_array($existingApp->getId(), $selectedApps)) { - $uoEntity->removeApp($existingApp); } } + } - // 2. Add newly selected apps + /** + * Ajoute les applications sélectionnées à l'utilisateur dans l'organisation. + * @param UsersOrganizations $uoEntity + * @param array $selectedApps + */ + private function addSelectedApps(UsersOrganizations $uoEntity, array $selectedApps): void + { foreach ($selectedApps as $appId) { $appEntity = $this->entityManager->getRepository(Apps::class)->find($appId); if ($appEntity && !$uoEntity->getApps()->contains($appEntity)) { $uoEntity->addApp($appEntity); } } - - $this->entityManager->persist($uoEntity); - $this->entityManager->flush(); } /** - * Deactivate all roles if role USER is deactivated - * @param User $user - * @param Organizations $organization + * Désactive tous les rôles d'un utilisateur dans une organisation. + * + * @param User $user L'utilisateur + * @param Organizations $organization L'organisation * @return void */ public function deactivateAllUserRoles(User $user, Organizations $organization): void