Easy_solution/src/Service/UserOrganizationService.php

251 lines
8.7 KiB
PHP

<?php
namespace App\Service;
use App\Entity\Apps;
use App\Entity\Organizations;
use App\Entity\Roles;
use App\Entity\User;
use App\Entity\UsersOrganizations;
use Doctrine\ORM\EntityManagerInterface;
readonly class UserOrganizationService
{
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.
*
* @param User $user The user whose organizations are being fetched
* @param int|null $organizationsId Optional organization ID to filter by
* @return array<array{organization:object, roles:Roles[], apps:object[]}>
*/
public function getUserOrganizations(User $user, int $organizationsId = null): array
{
$userOrganizations = $this->entityManager
->getRepository(UsersOrganizations::class)
->findAllDistinctOrganizationsByUserId($user->getId());
$organizations = [];
foreach ($userOrganizations as $uo) {
$orgId = $uo->getOrganization()->getId();
// If $organizationsId is provided, skip other organizations
if ($organizationsId !== null && $orgId !== $organizationsId) {
continue;
}
// Initialize the organization entry if it doesn't exist
$organizations[$orgId] = $organizations[$orgId] ?? $this->createEmptyOrganizationBucket($uo);
$organizations[$orgId]['uoId'] = $uo->getId();
// Aggregate roles & apps
$this->addRole($organizations[$orgId]['roles'], $uo->getRole());
$this->addApps($organizations[$orgId]['apps'], $uo->getApps());
}
$this->normalizeAppsIndexes($organizations);
return array_values($organizations);
}
/**
* Build the initial array structure for a fresh organization entry.
*
* @param UsersOrganizations $link
* @return array{organization:object, roles:Roles[], apps:array<int,object>}
*/
private function createEmptyOrganizationBucket(UsersOrganizations $link): array
{
return [
'organization' => $link->getOrganization(),
'roles' => [],
'apps' => [],
];
}
/**
* Add a Role entity to the roles array only if it is not already present (by ID).
*
* @param Roles[] &$roles
* @param Roles|null $role
*/
private function addRole(array &$roles, ?Roles $role): void
{
if ($role === null) {
return;
}
foreach ($roles as $existingRole) {
if ($existingRole->getId() === $role->getId()) {
return; // Already present
}
}
$roles[] = $role;
}
/**
* Merge one or many apps into the apps map, keeping only one entry per id.
*
* @param array<int,object> &$apps
* @param iterable $appsToAdd Collection returned by $userOrganizations->getApps()
*/
private function addApps(array &$apps, iterable $appsToAdd): void
{
foreach ($appsToAdd as $app) {
$apps[$app->getId()] = $apps[$app->getId()] ?? $app;
}
}
/**
* Convert apps from associative maps (keyed by id) to plain indexed arrays,
* so the final output is clean JSON-able.
*
* @param array &$organizations
*/
private function normalizeAppsIndexes(array &$organizations): void
{
foreach ($organizations as &$org) {
$org['apps'] = array_values($org['apps']);
}
}
/**
* Set user organizations with roles, ensuring USER role is always present
*
* @param User $user
* @param Organizations $organization
* @param array $selectedRoles Array of role IDs to set for the user in the organization
*/
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
]);
// 2. Build a map: roleId => UsersOrganizations entity
$currentRolesMap = [];
foreach ($currentUserOrgs as $uo) {
$currentRolesMap[$uo->getRole()->getId()] = $uo;
}
// 3. Check if we need to ensure USER role exists
$hasNonUserRole = false;
foreach ($selectedRoles as $roleId) {
if ($roleId !== $userRole->getId()) {
$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();
}
// 5. Add new roles that are selected but not present
foreach ($selectedRoles as $roleId) {
if (!isset($currentRolesMap[$roleId])) {
$roleEntity = $roleRepo->find($roleId);
if ($roleEntity) {
$newUserOrganization = new UsersOrganizations();
$newUserOrganization->setUsers($user);
$newUserOrganization->setRole($roleEntity);
$newUserOrganization->setOrganization($organization);
$newUserOrganization->setIsActive(true); // Ensure new roles are active
$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)
foreach ($currentRolesMap as $uo) {
$uo->setIsActive(false);
$this->entityManager->persist($uo);
}
$this->entityManager->flush();
}
public function setUserOrganizationsApps(User $user, Organizations $organization, array $selectedApps)
{
$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
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
* @return void
*/
public function deactivateAllUserRoles(User $user, Organizations $organization): void
{
$repo = $this->entityManager->getRepository(UsersOrganizations::class);
$userOrganizations = $repo->findBy([
'users' => $user,
'organization' => $organization
]);
foreach ($userOrganizations as $uo) {
$uo->setIsActive(false);
$this->entityManager->persist($uo);
}
$this->entityManager->flush();
}
}