From 9dc79eaa7d273e8042a6bff0b31bc6b40ac19cbe Mon Sep 17 00:00:00 2001 From: Charles Date: Tue, 12 Aug 2025 10:01:00 +0200 Subject: [PATCH] refonte role --- migrations/Version20250808085504.php | 64 +++++++ migrations/Version20250808091021.php | 46 +++++ migrations/Version20250811121053.php | 36 ++++ src/Entity/Apps.php | 67 ++++--- src/Entity/Organizations.php | 37 ++++ src/Entity/User.php | 2 - src/Entity/UserOrganizatonApp.php | 103 +++++++++++ src/Entity/UsersOrganizations.php | 83 ++++----- .../UserOrganizatonAppRepository.php | 43 +++++ .../UsersOrganizationsRepository.php | 171 +++++++----------- 10 files changed, 466 insertions(+), 186 deletions(-) create mode 100644 migrations/Version20250808085504.php create mode 100644 migrations/Version20250808091021.php create mode 100644 migrations/Version20250811121053.php create mode 100644 src/Entity/UserOrganizatonApp.php create mode 100644 src/Repository/UserOrganizatonAppRepository.php diff --git a/migrations/Version20250808085504.php b/migrations/Version20250808085504.php new file mode 100644 index 0000000..b1d4a31 --- /dev/null +++ b/migrations/Version20250808085504.php @@ -0,0 +1,64 @@ +addSql('CREATE TABLE user_organizaton_app (id SERIAL NOT NULL, users_id INT NOT NULL, organization_id INT DEFAULT NULL, role_id INT DEFAULT NULL, created_at TIMESTAMP(0) WITHOUT TIME ZONE NOT NULL, is_active BOOLEAN NOT NULL, PRIMARY KEY(id))'); + $this->addSql('CREATE INDEX IDX_2C952FC767B3B43D ON user_organizaton_app (users_id)'); + $this->addSql('CREATE INDEX IDX_2C952FC732C8A3DE ON user_organizaton_app (organization_id)'); + $this->addSql('CREATE INDEX IDX_2C952FC7D60322AC ON user_organizaton_app (role_id)'); + $this->addSql('COMMENT ON COLUMN user_organizaton_app.created_at IS \'(DC2Type:datetime_immutable)\''); + $this->addSql('ALTER TABLE user_organizaton_app ADD CONSTRAINT FK_2C952FC767B3B43D FOREIGN KEY (users_id) REFERENCES "user" (id) NOT DEFERRABLE INITIALLY IMMEDIATE'); + $this->addSql('ALTER TABLE user_organizaton_app ADD CONSTRAINT FK_2C952FC732C8A3DE FOREIGN KEY (organization_id) REFERENCES organizations (id) NOT DEFERRABLE INITIALLY IMMEDIATE'); + $this->addSql('ALTER TABLE user_organizaton_app ADD CONSTRAINT FK_2C952FC7D60322AC FOREIGN KEY (role_id) REFERENCES roles (id) NOT DEFERRABLE INITIALLY IMMEDIATE'); + $this->addSql('ALTER TABLE users_organizations_apps DROP CONSTRAINT fk_f01f6897964985f0'); + $this->addSql('ALTER TABLE users_organizations_apps DROP CONSTRAINT fk_f01f6897a2d76671'); + $this->addSql('ALTER TABLE apps_organizations DROP CONSTRAINT fk_ffe659d586288a55'); + $this->addSql('ALTER TABLE apps_organizations DROP CONSTRAINT fk_ffe659d5a2d76671'); + $this->addSql('DROP TABLE users_organizations_apps'); + $this->addSql('DROP TABLE apps_organizations'); + $this->addSql('ALTER TABLE users_organizations DROP CONSTRAINT fk_4b991472d60322ac'); + $this->addSql('DROP INDEX idx_4b991472d60322ac'); + $this->addSql('ALTER TABLE users_organizations DROP role_id'); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('CREATE SCHEMA public'); + $this->addSql('CREATE TABLE users_organizations_apps (users_organizations_id INT NOT NULL, apps_id INT NOT NULL, PRIMARY KEY(users_organizations_id, apps_id))'); + $this->addSql('CREATE INDEX idx_f01f6897964985f0 ON users_organizations_apps (users_organizations_id)'); + $this->addSql('CREATE INDEX idx_f01f6897a2d76671 ON users_organizations_apps (apps_id)'); + $this->addSql('CREATE TABLE apps_organizations (apps_id INT NOT NULL, organizations_id INT NOT NULL, PRIMARY KEY(apps_id, organizations_id))'); + $this->addSql('CREATE INDEX idx_ffe659d586288a55 ON apps_organizations (organizations_id)'); + $this->addSql('CREATE INDEX idx_ffe659d5a2d76671 ON apps_organizations (apps_id)'); + $this->addSql('ALTER TABLE users_organizations_apps ADD CONSTRAINT fk_f01f6897964985f0 FOREIGN KEY (users_organizations_id) REFERENCES users_organizations (id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE'); + $this->addSql('ALTER TABLE users_organizations_apps ADD CONSTRAINT fk_f01f6897a2d76671 FOREIGN KEY (apps_id) REFERENCES apps (id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE'); + $this->addSql('ALTER TABLE apps_organizations ADD CONSTRAINT fk_ffe659d586288a55 FOREIGN KEY (organizations_id) REFERENCES organizations (id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE'); + $this->addSql('ALTER TABLE apps_organizations ADD CONSTRAINT fk_ffe659d5a2d76671 FOREIGN KEY (apps_id) REFERENCES apps (id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE'); + $this->addSql('ALTER TABLE user_organizaton_app DROP CONSTRAINT FK_2C952FC767B3B43D'); + $this->addSql('ALTER TABLE user_organizaton_app DROP CONSTRAINT FK_2C952FC732C8A3DE'); + $this->addSql('ALTER TABLE user_organizaton_app DROP CONSTRAINT FK_2C952FC7D60322AC'); + $this->addSql('DROP TABLE user_organizaton_app'); + $this->addSql('ALTER TABLE users_organizations ADD role_id INT NOT NULL'); + $this->addSql('ALTER TABLE users_organizations ADD CONSTRAINT fk_4b991472d60322ac FOREIGN KEY (role_id) REFERENCES roles (id) NOT DEFERRABLE INITIALLY IMMEDIATE'); + $this->addSql('CREATE INDEX idx_4b991472d60322ac ON users_organizations (role_id)'); + } +} diff --git a/migrations/Version20250808091021.php b/migrations/Version20250808091021.php new file mode 100644 index 0000000..f88c599 --- /dev/null +++ b/migrations/Version20250808091021.php @@ -0,0 +1,46 @@ +addSql('ALTER TABLE user_organizaton_app DROP CONSTRAINT fk_2c952fc732c8a3de'); + $this->addSql('ALTER TABLE user_organizaton_app DROP CONSTRAINT fk_2c952fc767b3b43d'); + $this->addSql('DROP INDEX idx_2c952fc732c8a3de'); + $this->addSql('DROP INDEX idx_2c952fc767b3b43d'); + $this->addSql('ALTER TABLE user_organizaton_app DROP organization_id'); + $this->addSql('ALTER TABLE user_organizaton_app RENAME COLUMN users_id TO user_organization_id'); + $this->addSql('ALTER TABLE user_organizaton_app ADD CONSTRAINT FK_2C952FC72014CF51 FOREIGN KEY (user_organization_id) REFERENCES users_organizations (id) NOT DEFERRABLE INITIALLY IMMEDIATE'); + $this->addSql('CREATE INDEX IDX_2C952FC72014CF51 ON user_organizaton_app (user_organization_id)'); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('CREATE SCHEMA public'); + $this->addSql('ALTER TABLE user_organizaton_app DROP CONSTRAINT FK_2C952FC72014CF51'); + $this->addSql('DROP INDEX IDX_2C952FC72014CF51'); + $this->addSql('ALTER TABLE user_organizaton_app ADD organization_id INT DEFAULT NULL'); + $this->addSql('ALTER TABLE user_organizaton_app RENAME COLUMN user_organization_id TO users_id'); + $this->addSql('ALTER TABLE user_organizaton_app ADD CONSTRAINT fk_2c952fc732c8a3de FOREIGN KEY (organization_id) REFERENCES organizations (id) NOT DEFERRABLE INITIALLY IMMEDIATE'); + $this->addSql('ALTER TABLE user_organizaton_app ADD CONSTRAINT fk_2c952fc767b3b43d FOREIGN KEY (users_id) REFERENCES "user" (id) NOT DEFERRABLE INITIALLY IMMEDIATE'); + $this->addSql('CREATE INDEX idx_2c952fc732c8a3de ON user_organizaton_app (organization_id)'); + $this->addSql('CREATE INDEX idx_2c952fc767b3b43d ON user_organizaton_app (users_id)'); + } +} diff --git a/migrations/Version20250811121053.php b/migrations/Version20250811121053.php new file mode 100644 index 0000000..43d6837 --- /dev/null +++ b/migrations/Version20250811121053.php @@ -0,0 +1,36 @@ +addSql('ALTER TABLE user_organizaton_app ADD application_id INT NOT NULL'); + $this->addSql('ALTER TABLE user_organizaton_app ADD CONSTRAINT FK_2C952FC73E030ACD FOREIGN KEY (application_id) REFERENCES apps (id) NOT DEFERRABLE INITIALLY IMMEDIATE'); + $this->addSql('CREATE INDEX IDX_2C952FC73E030ACD ON user_organizaton_app (application_id)'); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('CREATE SCHEMA public'); + $this->addSql('ALTER TABLE user_organizaton_app DROP CONSTRAINT FK_2C952FC73E030ACD'); + $this->addSql('DROP INDEX IDX_2C952FC73E030ACD'); + $this->addSql('ALTER TABLE user_organizaton_app DROP application_id'); + } +} diff --git a/src/Entity/Apps.php b/src/Entity/Apps.php index d338949..9779a56 100644 --- a/src/Entity/Apps.php +++ b/src/Entity/Apps.php @@ -33,18 +33,18 @@ class Apps #[ORM\Column(options: ['default' => true])] private ?bool $isActive = null; - /** - * @var Collection - */ - #[ORM\ManyToMany(targetEntity: organizations::class, inversedBy: 'apps')] - private Collection $organization; - #[ORM\Column(length: 255, nullable: true)] private ?string $descriptionSmall = null; + /** + * @var Collection + */ + #[ORM\OneToMany(targetEntity: UserOrganizatonApp::class, mappedBy: 'application')] + private Collection $userOrganizatonApps; + public function __construct() { - $this->organization = new ArrayCollection(); + $this->userOrganizatonApps = new ArrayCollection(); } public function getId(): ?int @@ -124,29 +124,6 @@ class Apps return $this; } - /** - * @return Collection - */ - public function getOrganization(): Collection - { - return $this->organization; - } - - public function addOrganization(organizations $organization): static - { - if (!$this->organization->contains($organization)) { - $this->organization->add($organization); - } - - return $this; - } - - public function removeOrganization(organizations $organization): static - { - $this->organization->removeElement($organization); - - return $this; - } public function getDescriptionSmall(): ?string { @@ -159,4 +136,34 @@ class Apps return $this; } + + /** + * @return Collection + */ + public function getUserOrganizatonApps(): Collection + { + return $this->userOrganizatonApps; + } + + public function addUserOrganizatonApp(UserOrganizatonApp $userOrganizatonApp): static + { + if (!$this->userOrganizatonApps->contains($userOrganizatonApp)) { + $this->userOrganizatonApps->add($userOrganizatonApp); + $userOrganizatonApp->setApplication($this); + } + + return $this; + } + + public function removeUserOrganizatonApp(UserOrganizatonApp $userOrganizatonApp): static + { + if ($this->userOrganizatonApps->removeElement($userOrganizatonApp)) { + // set the owning side to null (unless already changed) + if ($userOrganizatonApp->getApplication() === $this) { + $userOrganizatonApp->setApplication(null); + } + } + + return $this; + } } diff --git a/src/Entity/Organizations.php b/src/Entity/Organizations.php index 7786c19..bff31ba 100644 --- a/src/Entity/Organizations.php +++ b/src/Entity/Organizations.php @@ -51,11 +51,18 @@ class Organizations #[ORM\OneToMany(targetEntity: Actions::class, mappedBy: 'Organization')] private Collection $actions; + /** + * @var Collection + */ + #[ORM\OneToMany(targetEntity: UserOrganizatonApp::class, mappedBy: 'organization')] + private Collection $userOrganizatonApps; + public function __construct() { $this->apps = new ArrayCollection(); $this->actions = new ArrayCollection(); $this->createdAt = new \DateTimeImmutable(); + $this->userOrganizatonApps = new ArrayCollection(); } public function getId(): ?int @@ -215,4 +222,34 @@ class Organizations return $this; } + + /** + * @return Collection + */ + public function getUserOrganizatonApps(): Collection + { + return $this->userOrganizatonApps; + } + + public function addUserOrganizatonApp(UserOrganizatonApp $userOrganizatonApp): static + { + if (!$this->userOrganizatonApps->contains($userOrganizatonApp)) { + $this->userOrganizatonApps->add($userOrganizatonApp); + $userOrganizatonApp->setOrganization($this); + } + + return $this; + } + + public function removeUserOrganizatonApp(UserOrganizatonApp $userOrganizatonApp): static + { + if ($this->userOrganizatonApps->removeElement($userOrganizatonApp)) { + // set the owning side to null (unless already changed) + if ($userOrganizatonApp->getOrganization() === $this) { + $userOrganizatonApp->setOrganization(null); + } + } + + return $this; + } } diff --git a/src/Entity/User.php b/src/Entity/User.php index 6797b7c..910ccb2 100644 --- a/src/Entity/User.php +++ b/src/Entity/User.php @@ -263,6 +263,4 @@ class User implements UserInterface, PasswordAuthenticatedUserInterface return $this; } - - } diff --git a/src/Entity/UserOrganizatonApp.php b/src/Entity/UserOrganizatonApp.php new file mode 100644 index 0000000..ee79cd5 --- /dev/null +++ b/src/Entity/UserOrganizatonApp.php @@ -0,0 +1,103 @@ +createdAt = new \DateTimeImmutable(); // Set createdAt to current time + $this->isActive = true; // Default value for isActive + } + + public function getId(): ?int + { + return $this->id; + } + + public function getCreatedAt(): ?\DateTimeImmutable + { + return $this->createdAt; + } + + public function setCreatedAt(\DateTimeImmutable $createdAt): static + { + $this->createdAt = $createdAt; + + return $this; + } + + public function getRole(): ?Roles + { + return $this->role; + } + + public function setRole(?Roles $role): static + { + $this->role = $role; + + return $this; + } + + public function isActive(): ?bool + { + return $this->isActive; + } + + public function setIsActive(bool $isActive): static + { + $this->isActive = $isActive; + + return $this; + } + + public function getUserOrganization(): ?UsersOrganizations + { + return $this->userOrganization; + } + + public function setUserOrganization(?UsersOrganizations $userOrganization): static + { + $this->userOrganization = $userOrganization; + + return $this; + } + + public function getApplication(): ?Apps + { + return $this->application; + } + + public function setApplication(?Apps $application): static + { + $this->application = $application; + + return $this; + } +} diff --git a/src/Entity/UsersOrganizations.php b/src/Entity/UsersOrganizations.php index 35cb2ad..722a189 100644 --- a/src/Entity/UsersOrganizations.php +++ b/src/Entity/UsersOrganizations.php @@ -17,33 +17,29 @@ class UsersOrganizations #[ORM\ManyToOne] #[ORM\JoinColumn(nullable: false)] - private ?user $users = null; + private ?User $users = null; #[ORM\ManyToOne] #[ORM\JoinColumn(nullable: false)] private ?Organizations $organization = null; - #[ORM\ManyToOne] - #[ORM\JoinColumn(nullable: false)] - private ?Roles $role = null; - #[ORM\Column(options: ['default' => true])] private ?bool $isActive = null; - /** - * @var Collection - */ - #[ORM\ManyToMany(targetEntity: Apps::class)] - private Collection $apps; - #[ORM\Column(nullable:true, options: ['default' => 'CURRENT_TIMESTAMP'])] private ?\DateTimeImmutable $createdAt = null; + /** + * @var Collection + */ + #[ORM\OneToMany(targetEntity: UserOrganizatonApp::class, mappedBy: 'userOrganization')] + private Collection $userOrganizatonApps; + public function __construct() { - $this->apps = new ArrayCollection(); $this->isActive = true; // Default value for isActive $this->createdAt = new \DateTimeImmutable(); // Set createdAt to current + $this->userOrganizatonApps = new ArrayCollection(); } public function getId(): ?int @@ -75,18 +71,6 @@ class UsersOrganizations return $this; } - public function getRole(): ?roles - { - return $this->role; - } - - public function setRole(?roles $role): static - { - $this->role = $role; - - return $this; - } - public function isActive(): ?bool { return $this->isActive; @@ -99,38 +83,37 @@ class UsersOrganizations return $this; } - /** - * @return Collection - */ - public function getApps(): Collection - { - return $this->apps; - } - - public function addApp(apps $app): static - { - if (!$this->apps->contains($app)) { - $this->apps->add($app); - } - - return $this; - } - - public function removeApp(apps $app): static - { - $this->apps->removeElement($app); - - return $this; - } - public function getCreatedAt(): ?\DateTimeImmutable { return $this->createdAt; } - public function setCreatedAt(?\DateTimeImmutable $createdAt): static + /** + * @return Collection + */ + public function getUserOrganizatonApps(): Collection { - $this->createdAt = $createdAt; + return $this->userOrganizatonApps; + } + + public function addUserOrganizatonApp(UserOrganizatonApp $userOrganizatonApp): static + { + if (!$this->userOrganizatonApps->contains($userOrganizatonApp)) { + $this->userOrganizatonApps->add($userOrganizatonApp); + $userOrganizatonApp->setUserOrganization($this); + } + + return $this; + } + + public function removeUserOrganizatonApp(UserOrganizatonApp $userOrganizatonApp): static + { + if ($this->userOrganizatonApps->removeElement($userOrganizatonApp)) { + // set the owning side to null (unless already changed) + if ($userOrganizatonApp->getUserOrganization() === $this) { + $userOrganizatonApp->setUserOrganization(null); + } + } return $this; } diff --git a/src/Repository/UserOrganizatonAppRepository.php b/src/Repository/UserOrganizatonAppRepository.php new file mode 100644 index 0000000..23a1f01 --- /dev/null +++ b/src/Repository/UserOrganizatonAppRepository.php @@ -0,0 +1,43 @@ + + */ +class UserOrganizatonAppRepository extends ServiceEntityRepository +{ + public function __construct(ManagerRegistry $registry) + { + parent::__construct($registry, UserOrganizatonApp::class); + } + +// /** +// * @return UserOrganizatonApp[] Returns an array of UserOrganizatonApp objects +// */ +// public function findByExampleField($value): array +// { +// return $this->createQueryBuilder('u') +// ->andWhere('u.exampleField = :val') +// ->setParameter('val', $value) +// ->orderBy('u.id', 'ASC') +// ->setMaxResults(10) +// ->getQuery() +// ->getResult() +// ; +// } + +// public function findOneBySomeField($value): ?UserOrganizatonApp +// { +// return $this->createQueryBuilder('u') +// ->andWhere('u.exampleField = :val') +// ->setParameter('val', $value) +// ->getQuery() +// ->getOneOrNullResult() +// ; +// } +} diff --git a/src/Repository/UsersOrganizationsRepository.php b/src/Repository/UsersOrganizationsRepository.php index a72cf62..bedbc5b 100644 --- a/src/Repository/UsersOrganizationsRepository.php +++ b/src/Repository/UsersOrganizationsRepository.php @@ -19,153 +19,116 @@ class UsersOrganizationsRepository extends ServiceEntityRepository parent::__construct($registry, UsersOrganizations::class); } - /** - * Find all distinct active organizations for a given user ID. - * - * @param int $userId - * @return UsersOrganizations[] - */ - public function findAllDistinctOrganizationsByUserId(int $userId): array - { - return $this->createQueryBuilder('uo') - ->select('DISTINCT uo') - ->leftJoin('uo.organization', 'o') - ->leftJoin('uo.role', 'r') - ->addSelect('o', 'r') - ->where('uo.users = :userId', 'uo.isActive = :isActive') - ->setParameter('userId', $userId) - ->setParameter('isActive', true) - ->getQuery() - ->getResult(); - } /** - * Find all organizations where a user (by email) has a specific role. - * + * Find organizations where a user has a specific role (e.g., ADMIN) + * * @param string $userEmail * @param string $roleName - * @return Organizations[] + * @return array Array of UsersOrganizations entities (with organizations preloaded) */ - public function findOrganizationsByUserEmailAndRoleName(string $userEmail, string $roleName): array + public function findUserOrganizationsByRole(string $userEmail, string $roleName): array { - $results = $this->createQueryBuilder('uo') + return $this->createQueryBuilder('uo') + ->select('uo, o') ->innerJoin('uo.users', 'u') ->innerJoin('uo.organization', 'o') - ->innerJoin('uo.role', 'r') + ->innerJoin('uo.userOrganizatonApps', 'uoa') + ->innerJoin('uoa.role', 'r') ->where('u.email = :email') ->andWhere('r.name = :roleName') ->andWhere('uo.isActive = :isActive') - ->andWhere('o.isActive = :orgIsActive') // Check if organization is active + ->andWhere('u.isActive = :userIsActive') + ->andWhere('o.isActive = :orgIsActive') + ->andWhere('uoa.isActive = :uoaIsActive') ->setParameter('email', $userEmail) ->setParameter('roleName', $roleName) ->setParameter('isActive', true) - ->setParameter('orgIsActive', true) // Parameter for organization active status + ->setParameter('userIsActive', true) + ->setParameter('orgIsActive', true) + ->setParameter('uoaIsActive', true) ->getQuery() ->getResult(); - - return array_map(fn($uo) => $uo->getOrganization(), $results); } - - /** - * Helper: Get all active UsersOrganizations links, optionally filtered by organizations. + * Get all active user-organization relationships. + * Optionally filter by specific organizations. * - * @param Organizations[]|null $organizations - * @return UsersOrganizations[] + * @param array|null $organizations Array of Organization entities to filter by + * @return array Array of UsersOrganizations entities */ public function getAllActiveUserOrganizationLinks(array $organizations = null): array { - $qb = $this->createQueryBuilder('uo') - ->innerJoin('uo.organization', 'o') - ->innerJoin('uo.users', 'u') - ->where('uo.isActive = :isActive') - ->setParameter('isActive', true); - - if (!empty($organizations)) { - $qb->andWhere('o IN (:organizations)') - ->setParameter('organizations', $organizations); - } - - return $qb->getQuery()->getResult(); - } - - - /** - * Get the last 10 new active users for a specific organization. - * Users are ordered by creation date (most recent first). - * - * @param Organizations $organization - * @return array - */ - public function getLastNewActiveUsersByOrganization(Organizations $organization): array - { - $results = $this->createQueryBuilder('uo') - ->select('u.id', 'u.surname', 'u.name', 'u.email', 'u.pictureUrl', 'u.isActive', 'uo.createdAt') + $queryBuilder = $this->createQueryBuilder('uo') ->innerJoin('uo.users', 'u') ->innerJoin('uo.organization', 'o') ->where('uo.isActive = :isActive') ->andWhere('u.isActive = :userIsActive') + ->andWhere('u.isDeleted = :userIsDeleted') ->andWhere('o.isActive = :orgIsActive') - ->andWhere('uo.organization = :organization') + ->andWhere('o.isDeleted = :orgIsDeleted') ->setParameter('isActive', true) ->setParameter('userIsActive', true) + ->setParameter('userIsDeleted', false) ->setParameter('orgIsActive', true) - ->setParameter('organization', $organization) - ->orderBy('uo.createdAt', 'DESC') - ->setMaxResults(10) - ->getQuery() - ->getResult(); + ->setParameter('orgIsDeleted', false) + ->orderBy('o.name', 'ASC') + ->addOrderBy('u.surname', 'ASC'); - // Remove duplicates by user ID (in case user has multiple roles) - $uniqueUsers = []; - foreach ($results as $result) { - $userId = $result['id']; - if (!isset($uniqueUsers[$userId])) { - $uniqueUsers[$userId] = $result; - } + // Filter by specific organizations if provided + if ($organizations !== null && !empty($organizations)) { + $queryBuilder->andWhere('uo.organization IN (:organizations)') + ->setParameter('organizations', $organizations); } - return array_values($uniqueUsers); + return $queryBuilder->getQuery()->getResult(); } /** - * Get all active admin users for a specific organization. - * Returns users who have the 'ADMIN' role in the given organization. + * Get all active users who are not in any organization. * - * @param Organizations $organization - * @return array + * @return array Array of User entities */ - public function getAdminUsersByOrganization(Organizations $organization): array + public function getUsersWithoutOrganizations(): array { - $results = $this->createQueryBuilder('uo') - ->select('u.id', 'u.surname', 'u.name', 'u.email', 'u.pictureUrl', 'u.isActive') - ->innerJoin('uo.users', 'u') - ->innerJoin('uo.organization', 'o') - ->innerJoin('uo.role', 'r') - ->where('uo.isActive = :isActive') - ->andWhere('u.isActive = :userIsActive') - ->andWhere('o.isActive = :orgIsActive') - ->andWhere('uo.organization = :organization') - ->andWhere('r.name = :roleName') - ->setParameter('isActive', true) + return $this->getEntityManager()->getRepository(User::class) + ->createQueryBuilder('u') + ->leftJoin('App\Entity\UsersOrganizations', 'uo', 'WITH', 'uo.users = u AND uo.isActive = true') + ->where('u.isActive = :userIsActive') + ->andWhere('u.isDeleted = :userIsDeleted') + ->andWhere('uo.id IS NULL') ->setParameter('userIsActive', true) - ->setParameter('orgIsActive', true) - ->setParameter('organization', $organization) - ->setParameter('roleName', 'ADMIN') + ->setParameter('userIsDeleted', false) ->orderBy('u.surname', 'ASC') ->getQuery() ->getResult(); - - // Remove duplicates by user ID (in case user has multiple admin-related roles) - $uniqueUsers = []; - foreach ($results as $result) { - $userId = $result['id']; - if (!isset($uniqueUsers[$userId])) { - $uniqueUsers[$userId] = $result; - } - } - - return array_values($uniqueUsers); } + +// /** +// * Get the last 10 new active users for a specific organization. +// * Users are ordered by creation date (most recent first). +// * +// * @param Organizations $organization +// * @return array +// */ +// public function getLastNewActiveUsersByOrganization(Organizations $organization): array +// { +// return $this->createQueryBuilder('uo') +// ->select('u.id', 'u.surname', 'u.name', 'u.email', 'u.pictureUrl', 'u.isActive', 'uo.createdAt') +// ->innerJoin('uo.users', 'u') +// ->innerJoin('uo.organization', 'o') +// ->where('uo.isActive = :isActive') +// ->andWhere('u.isActive = :userIsActive') +// ->andWhere('o.isActive = :orgIsActive') +// ->andWhere('uo.organization = :organization') +// ->setParameter('isActive', true) +// ->setParameter('userIsActive', true) +// ->setParameter('orgIsActive', true) +// ->setParameter('organization', $organization) +// ->orderBy('uo.createdAt', 'DESC') +// ->setMaxResults(10) +// ->getQuery() +// ->getResult(); +// } }