251 lines
8.7 KiB
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();
|
|
}
|
|
}
|