repository = $entityManager->getRepository(User::class); } public function countBy(array $criteria): int { return $this->repository->count($criteria); } public function countByActive(): int { return $this->countBy(['enabled' => true]); } public function countByNotHavingAttribute(string $key): int { $rsm = new ResultSetMapping(); $rsm->addScalarResult('count', 'count'); $sql = 'SELECT count(*) FROM users u WHERE NOT attributes ?? :key OR attributes IS NULL AND enabled IS TRUE'; return $this->entityManager->createNativeQuery($sql, $rsm)->setParameter(':key', $key)->getSingleScalarResult(); } public function countByUsernameOrEmail(string $pattern): int { $qb = $this->queryByUsernameOrEmail($pattern); $qb->select('COUNT(u)'); return (int) $qb->getQuery()->getSingleScalarResult(); } public function find($id, $lockMode = null, $lockVersion = null): ?User { return $this->repository->find($id, $lockMode, $lockVersion); } /** * @return User[] */ public function findAll(): array { return $this->repository->findAll(); } /** * @throws Exception */ public function findAllAsArray(string $lang): iterable { $sql = sprintf(<<<'SQL' SELECT u.id, u.username AS username, u.email AS email, u.enabled, u.civility_id, civility.abbreviation->>:lang AS civility_abbreviation, civility.name->>:lang AS civility_name, u.label, mainCenter.id AS mainCenter_id, mainCenter.name AS mainCenter_name, mainScope.id AS mainScope_id, mainScope.name->>:lang AS mainScope_name, userJob.id AS userJob_id, userJob.label->>:lang AS userJob_name, currentLocation.id AS currentLocation_id, currentLocation.name AS currentLocation_name, mainLocation.id AS mainLocation_id, mainLocation.name AS mainLocation_name, u.absenceStart FROM users u LEFT JOIN chill_main_civility civility ON u.civility_id = civility.id LEFT JOIN centers mainCenter ON u.maincenter_id = mainCenter.id LEFT JOIN chill_main_user_job_history userJobHistory ON u.id = userJobHistory.user_id AND tstzrange(userJobHistory.startdate, userJobHistory.enddate) @> NOW() LEFT JOIN chill_main_user_job userJob ON userJobHistory.job_id = userJob.id LEFT JOIN chill_main_user_scope_history userScopeHistory ON u.id = userScopeHistory.user_id AND tstzrange(userScopeHistory.startdate, userScopeHistory.enddate) @> NOW() LEFT JOIN scopes mainScope ON userScopeHistory.scope_id = mainScope.id LEFT JOIN chill_main_location currentLocation ON u.currentlocation_id = currentLocation.id LEFT JOIN chill_main_location mainLocation ON u.mainlocation_id = mainLocation.id ORDER BY u.label, u.id SQL); $query = $this->connection->prepare($sql); foreach ($query->executeQuery(['lang' => $lang])->iterateAssociative() as $u) { $converted = []; foreach (self::FIELDS as $f) { $converted[$f] = $u[strtolower($f)]; } $converted['absenceStart'] = null !== $u['absencestart'] ? new \DateTimeImmutable($u['absencestart']) : null; /* @phpstan-ignore-next-line phpstan does not take into account that all required keys will be present */ yield $converted; } } public function findAllUserACLAsArray(): iterable { $sql = <<<'SQL' SELECT u.id, u.username, u.email, u.label, u.enabled, c.id AS center_id, c.name AS center_name, pg.id AS permissionsGroup_id, pg.name AS permissionsGroup_name FROM users u LEFT JOIN user_groupcenter ON u.id = user_groupcenter.user_id LEFT JOIN group_centers ON user_groupcenter.groupcenter_id = group_centers.id LEFT JOIN centers c on group_centers.center_id = c.id LEFT JOIN permission_groups pg on group_centers.permissionsgroup_id = pg.id ORDER BY u.username, c.name, pg.name SQL; $query = $this->entityManager->getConnection()->executeQuery($sql); foreach ($query->iterateAssociative() as $u) { yield $u; } } /** * @param mixed|null $limit * @param mixed|null $offset * * @return User[] */ public function findBy(array $criteria, ?array $orderBy = null, $limit = null, $offset = null): array { return $this->repository->findBy($criteria, $orderBy, $limit, $offset); } /** * @return array|User[] */ public function findByActive(?array $orderBy = null, ?int $limit = null, ?int $offset = null): array { return $this->findBy(['enabled' => true], $orderBy, $limit, $offset); } /** * Find users which does not have a key on attribute column. * * @return array|User[] */ public function findByNotHavingAttribute(string $key, ?int $limit = null, ?int $offset = null): array { $rsm = new ResultSetMappingBuilder($this->entityManager); $rsm->addRootEntityFromClassMetadata(User::class, 'u'); $sql = 'SELECT '.$rsm->generateSelectClause().' FROM users u WHERE NOT attributes ?? :key OR attributes IS NULL AND enabled IS TRUE'; if (null !== $limit) { $sql .= " LIMIT {$limit}"; } if (null !== $offset) { $sql .= " OFFSET {$offset}"; } return $this->entityManager->createNativeQuery($sql, $rsm)->setParameter(':key', $key)->getResult(); } public function findByUsernameOrEmail(string $pattern, ?array $orderBy = [], ?int $limit = null, ?int $offset = null): array { $qb = $this->queryByUsernameOrEmail($pattern); $qb->select('u'); if (null !== $limit) { $qb->setMaxResults($limit); } if (null !== $offset) { $qb->setFirstResult($offset); } foreach ($orderBy as $field => $order) { $qb->addOrderBy('u.'.$field, $order); } return $qb->getQuery()->getResult(); } public function findOneBy(array $criteria, ?array $orderBy = null): ?User { return $this->repository->findOneBy($criteria, $orderBy); } public function findOneByUsernameOrEmail(string $pattern): ?User { $qb = $this->queryByUsernameOrEmail($pattern)->select('u'); try { return $qb->getQuery()->getSingleResult(); } catch (NoResultException) { return null; } } /** * Get the users having a specific flags. * * If provided, only the users amongst "filtered users" are searched. This * allows to make a first search amongst users based on role and center * and, then filter those users having some flags. * * @param \Chill\MainBundle\Entity\User[] $amongstUsers */ public function findUsersHavingFlags($flag, array $amongstUsers = []): array { $gcs = $this ->entityManager ->createQuery( 'SELECT DISTINCT gc '. 'FROM '.GroupCenter::class.' gc '. 'JOIN gc.permissionsGroup pg '. 'WHERE '. 'JSONB_EXISTS_IN_ARRAY(pg.flags, :flag) = :true ' ) ->setParameters([ 'true' => true, 'flag' => $flag, ]) ->getResult(); if (0 === \count($gcs)) { return []; } $qb = $this->entityManager->createQueryBuilder(); $qb ->select('DISTINCT u') ->from(User::class, 'u') ->where("u.enabled = 'TRUE'"); $orx = $qb->expr()->orX(); foreach ($gcs as $i => $gc) { $orx->add(':gc_'.$i.' MEMBER OF u.groupCenters'); $qb->setParameter('gc_'.$i, $gc); } $qb->andWhere($orx); if ([] !== $amongstUsers) { $qb ->andWhere($qb->expr()->in('u', ':amongstUsers')) ->setParameter('amongstUsers', $amongstUsers); } return $qb->getQuery()->getResult(); } public function getClassName(): string { return User::class; } public function getResult( QueryBuilder $qb, ?int $start = 0, ?int $limit = 50, ?array $orderBy = [], ): array { $qb->select('u'); $qb ->setFirstResult($start) ->setMaxResults($limit); foreach ($orderBy as $field => $direction) { $qb->addOrderBy('u.'.$field, $direction); } return $qb->getQuery()->getResult(); } private function queryByUsernameOrEmail(string $pattern): QueryBuilder { $qb = $this->entityManager->createQueryBuilder()->from(User::class, 'u'); $searchByPattern = $qb->expr()->orX(); $searchByPattern ->add($qb->expr()->like('u.usernameCanonical', 'CONCAT(\'%\', LOWER(UNACCENT(:pattern)), \'%\')')) ->add($qb->expr()->like('u.emailCanonical', 'CONCAT(\'%\', LOWER(UNACCENT(:pattern)), \'%\')')); $qb ->where($searchByPattern) ->setParameter('pattern', $pattern); return $qb; } public function buildFilterBaseQuery(?string $queryString, array $isActive) { if (null !== $queryString) { $qb = $this->queryByUsernameOrEmail($queryString); } else { $qb = $this->entityManager->createQueryBuilder()->from(User::class, 'u'); } // Add condition based on active/inactive status if (in_array('Active', $isActive, true) && !in_array('Inactive', $isActive, true)) { $qb->andWhere('u.enabled = true'); } elseif (in_array('Inactive', $isActive, true) && !in_array('Active', $isActive, true)) { $qb->andWhere('u.enabled = false'); } return $qb; } public function findFilteredUsers( ?string $queryString = null, array $isActive = ['active'], ?int $start = 0, ?int $limit = 50, ?array $orderBy = ['username' => 'ASC'], ): array { $qb = $this->buildFilterBaseQuery($queryString, $isActive); return $this->getResult($qb, $start, $limit, $orderBy); } public function countFilteredUsers( ?string $queryString = null, array $isActive = ['active'], ): int { $qb = $this->buildFilterBaseQuery($queryString, $isActive); try { return $qb ->select('COUNT(u)') ->getQuery()->getSingleScalarResult(); } catch (NoResultException|NonUniqueResultException $e) { throw new \LogicException('a count query should return one result', previous: $e); } } }