diff --git a/src/Bundle/ChillPersonBundle/Repository/SocialWork/EvaluationRepository.php b/src/Bundle/ChillPersonBundle/Repository/SocialWork/EvaluationRepository.php index b09ee57b8..e97640e4b 100644 --- a/src/Bundle/ChillPersonBundle/Repository/SocialWork/EvaluationRepository.php +++ b/src/Bundle/ChillPersonBundle/Repository/SocialWork/EvaluationRepository.php @@ -18,12 +18,13 @@ use Doctrine\ORM\EntityRepository; use Doctrine\ORM\NonUniqueResultException; use Doctrine\ORM\NoResultException; use Doctrine\ORM\QueryBuilder; +use Symfony\Component\HttpFoundation\RequestStack; final readonly class EvaluationRepository implements EvaluationRepositoryInterface { private EntityRepository $repository; - public function __construct(private EntityManagerInterface $entityManager) + public function __construct(private EntityManagerInterface $entityManager, private RequestStack $requestStack) { $this->repository = $entityManager->getRepository(Evaluation::class); } @@ -70,6 +71,11 @@ final readonly class EvaluationRepository implements EvaluationRepositoryInterfa return Evaluation::class; } + private function getLang(): string + { + return $this->requestStack->getCurrentRequest()?->getLocale() ?? 'fr'; + } + public function getResult( QueryBuilder $qb, ?int $start = 0, @@ -93,9 +99,11 @@ final readonly class EvaluationRepository implements EvaluationRepositoryInterfa { $qb = $this->entityManager->createQueryBuilder()->from(Evaluation::class, 'e'); - $qb->expr()->like('e.title', 'CONCAT(\'%\', LOWER(UNACCENT(:pattern)), \'%\')'); - - $qb->setParameter('pattern', $pattern); + // Extract the current locale's value from the JSON `title` and search on it + $qb + ->where($qb->expr()->like('LOWER(UNACCENT(JSON_EXTRACT(e.title, :lang)))', "CONCAT('%', LOWER(UNACCENT(:pattern)), '%')")) + ->setParameter('pattern', $pattern) + ->setParameter('lang', $this->getLang()); return $qb; } diff --git a/src/Bundle/ChillPersonBundle/Repository/SocialWork/GoalRepository.php b/src/Bundle/ChillPersonBundle/Repository/SocialWork/GoalRepository.php index ec89c597d..ac25445f4 100644 --- a/src/Bundle/ChillPersonBundle/Repository/SocialWork/GoalRepository.php +++ b/src/Bundle/ChillPersonBundle/Repository/SocialWork/GoalRepository.php @@ -15,14 +15,17 @@ use Chill\PersonBundle\Entity\SocialWork\Goal; use Chill\PersonBundle\Entity\SocialWork\SocialAction; use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityRepository; +use Doctrine\ORM\NonUniqueResultException; +use Doctrine\ORM\NoResultException; use Doctrine\ORM\QueryBuilder; use Doctrine\Persistence\ObjectRepository; +use Symfony\Component\HttpFoundation\RequestStack; final readonly class GoalRepository implements ObjectRepository { private EntityRepository $repository; - public function __construct(EntityManagerInterface $entityManager) + public function __construct(private EntityManagerInterface $entityManager, private RequestStack $requestStack) { $this->repository = $entityManager->getRepository(Goal::class); } @@ -101,6 +104,102 @@ final readonly class GoalRepository implements ObjectRepository return Goal::class; } + private function getLang(): string + { + return $this->requestStack->getCurrentRequest()?->getLocale() ?? 'fr'; + } + + public function getResult( + QueryBuilder $qb, + ?int $start = 0, + ?int $limit = 50, + ?array $orderBy = [], + ): array { + $qb->select('g'); + + $qb + ->setFirstResult($start) + ->setMaxResults($limit); + + foreach ($orderBy as $field => $direction) { + $qb->addOrderBy('g.'.$field, $direction); + } + + return $qb->getQuery()->getResult(); + } + + private function queryByTitle(string $pattern): QueryBuilder + { + $qb = $this->entityManager->createQueryBuilder()->from(Goal::class, 'g'); + + // search across locales by extracting the localized value + $qb + ->where($qb->expr()->like('LOWER(UNACCENT(JSON_EXTRACT(g.title, :lang)))', "CONCAT('%', LOWER(UNACCENT(:pattern)), '%')")) + ->setParameter('pattern', $pattern) + ->setParameter('lang', $this->getLang()); + + return $qb; + } + + public function buildFilterBaseQuery(?string $queryString, array $isActive): QueryBuilder + { + if (null !== $queryString) { + $qb = $this->queryByTitle($queryString); + } else { + $qb = $this->entityManager->createQueryBuilder()->from(Goal::class, 'g'); + } + + // Active when desactivationDate is null or in the future + $now = new \DateTime('now'); + if (in_array('Active', $isActive, true) && !in_array('Inactive', $isActive, true)) { + $qb->andWhere( + $qb->expr()->orX( + $qb->expr()->isNull('g.desactivationDate'), + $qb->expr()->gt('g.desactivationDate', ':now') + ) + )->setParameter('now', $now); + } elseif (in_array('Inactive', $isActive, true) && !in_array('Active', $isActive, true)) { + $qb->andWhere( + $qb->expr()->andX( + $qb->expr()->isNotNull('g.desactivationDate'), + $qb->expr()->lte('g.desactivationDate', ':now') + ) + )->setParameter('now', $now); + } + + return $qb; + } + + /** + * @return array + */ + public function findFilteredGoals( + ?string $queryString = null, + array $isActive = ['active'], + ?int $start = 0, + ?int $limit = 50, + ?array $orderBy = ['id' => 'ASC'], + ): array { + $qb = $this->buildFilterBaseQuery($queryString, $isActive); + + return $this->getResult($qb, $start, $limit, $orderBy); + } + + public function countFilteredGoals( + ?string $queryString = null, + array $isActive = ['active'], + ): int { + $qb = $this->buildFilterBaseQuery($queryString, $isActive); + + try { + return $qb + ->select('COUNT(g)') + ->getQuery()->getSingleScalarResult(); + } catch (NoResultException|NonUniqueResultException $e) { + throw new \LogicException('a count query should return one result', previous: $e); + } + } + private function buildQueryBySocialActionWithDescendants(SocialAction $action): QueryBuilder { $actions = $action->getDescendantsWithThis(); diff --git a/src/Bundle/ChillPersonBundle/Repository/SocialWork/ResultRepository.php b/src/Bundle/ChillPersonBundle/Repository/SocialWork/ResultRepository.php index 0d470d2d7..9e80ec491 100644 --- a/src/Bundle/ChillPersonBundle/Repository/SocialWork/ResultRepository.php +++ b/src/Bundle/ChillPersonBundle/Repository/SocialWork/ResultRepository.php @@ -16,14 +16,17 @@ use Chill\PersonBundle\Entity\SocialWork\Result; use Chill\PersonBundle\Entity\SocialWork\SocialAction; use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityRepository; +use Doctrine\ORM\NonUniqueResultException; +use Doctrine\ORM\NoResultException; use Doctrine\ORM\QueryBuilder; use Doctrine\Persistence\ObjectRepository; +use Symfony\Component\HttpFoundation\RequestStack; final readonly class ResultRepository implements ObjectRepository { private EntityRepository $repository; - public function __construct(EntityManagerInterface $entityManager) + public function __construct(private EntityManagerInterface $entityManager, private RequestStack $requestStack) { $this->repository = $entityManager->getRepository(Result::class); } @@ -125,6 +128,100 @@ final readonly class ResultRepository implements ObjectRepository return Result::class; } + private function getLang(): string + { + return $this->requestStack->getCurrentRequest()?->getLocale() ?? 'fr'; + } + + public function getResult( + QueryBuilder $qb, + ?int $start = 0, + ?int $limit = 50, + ?array $orderBy = [], + ): array { + $qb->select('r'); + + $qb + ->setFirstResult($start) + ->setMaxResults($limit); + + foreach ($orderBy as $field => $direction) { + $qb->addOrderBy('r.'.$field, $direction); + } + + return $qb->getQuery()->getResult(); + } + + private function queryByTitle(string $pattern): QueryBuilder + { + $qb = $this->entityManager->createQueryBuilder()->from(Result::class, 'r'); + + $qb + ->where($qb->expr()->like('LOWER(UNACCENT(JSON_EXTRACT(r.title, :lang)))', "CONCAT('%', LOWER(UNACCENT(:pattern)), '%')")) + ->setParameter('pattern', $pattern) + ->setParameter('lang', $this->getLang()); + + return $qb; + } + + public function buildFilterBaseQuery(?string $queryString, array $isActive): QueryBuilder + { + if (null !== $queryString) { + $qb = $this->queryByTitle($queryString); + } else { + $qb = $this->entityManager->createQueryBuilder()->from(Result::class, 'r'); + } + + $now = new \DateTime('now'); + if (in_array('Active', $isActive, true) && !in_array('Inactive', $isActive, true)) { + $qb->andWhere( + $qb->expr()->orX( + $qb->expr()->isNull('r.desactivationDate'), + $qb->expr()->gt('r.desactivationDate', ':now') + ) + )->setParameter('now', $now); + } elseif (in_array('Inactive', $isActive, true) && !in_array('Active', $isActive, true)) { + $qb->andWhere( + $qb->expr()->andX( + $qb->expr()->isNotNull('r.desactivationDate'), + $qb->expr()->lte('r.desactivationDate', ':now') + ) + )->setParameter('now', $now); + } + + return $qb; + } + + /** + * @return array + */ + public function findFilteredResults( + ?string $queryString = null, + array $isActive = ['active'], + ?int $start = 0, + ?int $limit = 50, + ?array $orderBy = ['id' => 'ASC'], + ): array { + $qb = $this->buildFilterBaseQuery($queryString, $isActive); + + return $this->getResult($qb, $start, $limit, $orderBy); + } + + public function countFilteredResults( + ?string $queryString = null, + array $isActive = ['active'], + ): int { + $qb = $this->buildFilterBaseQuery($queryString, $isActive); + + try { + return $qb + ->select('COUNT(r)') + ->getQuery()->getSingleScalarResult(); + } catch (NoResultException|NonUniqueResultException $e) { + throw new \LogicException('a count query should return one result', previous: $e); + } + } + private function buildQueryByGoal(Goal $goal): QueryBuilder { $qb = $this->repository->createQueryBuilder('r'); diff --git a/src/Bundle/ChillPersonBundle/Repository/SocialWork/SocialActionRepository.php b/src/Bundle/ChillPersonBundle/Repository/SocialWork/SocialActionRepository.php index 4d37cb3d4..81da2c60c 100644 --- a/src/Bundle/ChillPersonBundle/Repository/SocialWork/SocialActionRepository.php +++ b/src/Bundle/ChillPersonBundle/Repository/SocialWork/SocialActionRepository.php @@ -14,14 +14,17 @@ namespace Chill\PersonBundle\Repository\SocialWork; use Chill\PersonBundle\Entity\SocialWork\SocialAction; use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityRepository; +use Doctrine\ORM\NonUniqueResultException; +use Doctrine\ORM\NoResultException; use Doctrine\ORM\QueryBuilder; use Doctrine\Persistence\ObjectRepository; +use Symfony\Component\HttpFoundation\RequestStack; final readonly class SocialActionRepository implements ObjectRepository { private EntityRepository $repository; - public function __construct(EntityManagerInterface $entityManager) + public function __construct(private EntityManagerInterface $entityManager, private RequestStack $requestStack) { $this->repository = $entityManager->getRepository(SocialAction::class); } @@ -84,6 +87,100 @@ final readonly class SocialActionRepository implements ObjectRepository return SocialAction::class; } + private function getLang(): string + { + return $this->requestStack->getCurrentRequest()?->getLocale() ?? 'fr'; + } + + public function getResult( + QueryBuilder $qb, + ?int $start = 0, + ?int $limit = 50, + ?array $orderBy = [], + ): array { + $qb->select('sa'); + + $qb + ->setFirstResult($start) + ->setMaxResults($limit); + + foreach ($orderBy as $field => $direction) { + $qb->addOrderBy('sa.'.$field, $direction); + } + + return $qb->getQuery()->getResult(); + } + + private function queryByTitle(string $pattern): QueryBuilder + { + $qb = $this->entityManager->createQueryBuilder()->from(SocialAction::class, 'sa'); + + $qb + ->where($qb->expr()->like('LOWER(UNACCENT(JSON_EXTRACT(sa.title, :lang)))', "CONCAT('%', LOWER(UNACCENT(:pattern)), '%')")) + ->setParameter('pattern', $pattern) + ->setParameter('lang', $this->getLang()); + + return $qb; + } + + public function buildFilterBaseQuery(?string $queryString, array $isActive): QueryBuilder + { + if (null !== $queryString) { + $qb = $this->queryByTitle($queryString); + } else { + $qb = $this->entityManager->createQueryBuilder()->from(SocialAction::class, 'sa'); + } + + $now = new \DateTime('now'); + if (in_array('Active', $isActive, true) && !in_array('Inactive', $isActive, true)) { + $qb->andWhere( + $qb->expr()->orX( + $qb->expr()->isNull('sa.desactivationDate'), + $qb->expr()->gt('sa.desactivationDate', ':now') + ) + )->setParameter('now', $now); + } elseif (in_array('Inactive', $isActive, true) && !in_array('Active', $isActive, true)) { + $qb->andWhere( + $qb->expr()->andX( + $qb->expr()->isNotNull('sa.desactivationDate'), + $qb->expr()->lte('sa.desactivationDate', ':now') + ) + )->setParameter('now', $now); + } + + return $qb; + } + + /** + * @return array + */ + public function findFilteredSocialActions( + ?string $queryString = null, + array $isActive = ['active'], + ?int $start = 0, + ?int $limit = 50, + ?array $orderBy = ['ordering' => 'ASC'], + ): array { + $qb = $this->buildFilterBaseQuery($queryString, $isActive); + + return $this->getResult($qb, $start, $limit, $orderBy); + } + + public function countFilteredSocialActions( + ?string $queryString = null, + array $isActive = ['active'], + ): int { + $qb = $this->buildFilterBaseQuery($queryString, $isActive); + + try { + return $qb + ->select('COUNT(sa)') + ->getQuery()->getSingleScalarResult(); + } catch (NoResultException|NonUniqueResultException $e) { + throw new \LogicException('a count query should return one result', previous: $e); + } + } + private function buildQueryWithDesactivatedDateCriteria(): QueryBuilder { $qb = $this->repository->createQueryBuilder('sa'); diff --git a/src/Bundle/ChillPersonBundle/Repository/SocialWork/SocialIssueRepository.php b/src/Bundle/ChillPersonBundle/Repository/SocialWork/SocialIssueRepository.php index 40ec35855..638e1a869 100644 --- a/src/Bundle/ChillPersonBundle/Repository/SocialWork/SocialIssueRepository.php +++ b/src/Bundle/ChillPersonBundle/Repository/SocialWork/SocialIssueRepository.php @@ -14,14 +14,17 @@ namespace Chill\PersonBundle\Repository\SocialWork; use Chill\PersonBundle\Entity\SocialWork\SocialIssue; use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityRepository; +use Doctrine\ORM\NonUniqueResultException; +use Doctrine\ORM\NoResultException; use Doctrine\ORM\QueryBuilder; use Doctrine\Persistence\ObjectRepository; +use Symfony\Component\HttpFoundation\RequestStack; final readonly class SocialIssueRepository implements ObjectRepository { private EntityRepository $repository; - public function __construct(EntityManagerInterface $entityManager) + public function __construct(private EntityManagerInterface $entityManager, private RequestStack $requestStack) { $this->repository = $entityManager->getRepository(SocialIssue::class); } @@ -79,6 +82,100 @@ final readonly class SocialIssueRepository implements ObjectRepository return SocialIssue::class; } + public function getResult( + QueryBuilder $qb, + ?int $start = 0, + ?int $limit = 50, + ?array $orderBy = [], + ): array { + $qb->select('si'); + + $qb + ->setFirstResult($start) + ->setMaxResults($limit); + + foreach ($orderBy as $field => $direction) { + $qb->addOrderBy('si.'.$field, $direction); + } + + return $qb->getQuery()->getResult(); + } + + private function getLang(): string + { + return $this->requestStack->getCurrentRequest()?->getLocale() ?? 'fr'; + } + + private function queryByTitle(string $pattern): QueryBuilder + { + $qb = $this->entityManager->createQueryBuilder()->from(SocialIssue::class, 'si'); + + $qb + ->where($qb->expr()->like('LOWER(UNACCENT(JSON_EXTRACT(si.title, :lang)))', "CONCAT('%', LOWER(UNACCENT(:pattern)), '%')")) + ->setParameter('pattern', $pattern) + ->setParameter('lang', $this->getLang()); + + return $qb; + } + + public function buildFilterBaseQuery(?string $queryString, array $isActive): QueryBuilder + { + if (null !== $queryString) { + $qb = $this->queryByTitle($queryString); + } else { + $qb = $this->entityManager->createQueryBuilder()->from(SocialIssue::class, 'si'); + } + + $now = new \DateTime('now'); + if (in_array('Active', $isActive, true) && !in_array('Inactive', $isActive, true)) { + $qb->andWhere( + $qb->expr()->orX( + $qb->expr()->isNull('si.desactivationDate'), + $qb->expr()->gt('si.desactivationDate', ':now') + ) + )->setParameter('now', $now); + } elseif (in_array('Inactive', $isActive, true) && !in_array('Active', $isActive, true)) { + $qb->andWhere( + $qb->expr()->andX( + $qb->expr()->isNotNull('si.desactivationDate'), + $qb->expr()->lte('si.desactivationDate', ':now') + ) + )->setParameter('now', $now); + } + + return $qb; + } + + /** + * @return array + */ + public function findFilteredSocialIssues( + ?string $queryString = null, + array $isActive = ['active'], + ?int $start = 0, + ?int $limit = 50, + ?array $orderBy = ['ordering' => 'ASC'], + ): array { + $qb = $this->buildFilterBaseQuery($queryString, $isActive); + + return $this->getResult($qb, $start, $limit, $orderBy); + } + + public function countFilteredSocialIssues( + ?string $queryString = null, + array $isActive = ['active'], + ): int { + $qb = $this->buildFilterBaseQuery($queryString, $isActive); + + try { + return $qb + ->select('COUNT(si)') + ->getQuery()->getSingleScalarResult(); + } catch (NoResultException|NonUniqueResultException $e) { + throw new \LogicException('a count query should return one result', previous: $e); + } + } + private function buildQueryWithDesactivatedDateCriteria(): QueryBuilder { $qb = $this->repository->createQueryBuilder('si');