From 029524ba2c2b333981bbe058ed07822ea4c9a966 Mon Sep 17 00:00:00 2001 From: LenaertsJ Date: Fri, 5 Jul 2024 08:52:46 +0000 Subject: [PATCH] Resolve "Add active/inactive filter to user list in admin" --- .../unreleased/Feature-20240705-104634.yaml | 5 ++ .../Controller/UserController.php | 46 +++++----- .../Repository/UserRepository.php | 87 ++++++++++++++++++- .../Tests/Repository/UserRepositoryTest.php | 55 ++++++++++++ 4 files changed, 166 insertions(+), 27 deletions(-) create mode 100644 .changes/unreleased/Feature-20240705-104634.yaml create mode 100644 src/Bundle/ChillMainBundle/Tests/Repository/UserRepositoryTest.php diff --git a/.changes/unreleased/Feature-20240705-104634.yaml b/.changes/unreleased/Feature-20240705-104634.yaml new file mode 100644 index 000000000..bb21021be --- /dev/null +++ b/.changes/unreleased/Feature-20240705-104634.yaml @@ -0,0 +1,5 @@ +kind: Feature +body: '[admin] filter users by active / inactive in the admin user''s list' +time: 2024-07-05T10:46:34.365091722+02:00 +custom: + Issue: "" diff --git a/src/Bundle/ChillMainBundle/Controller/UserController.php b/src/Bundle/ChillMainBundle/Controller/UserController.php index b335b356a..6869f3148 100644 --- a/src/Bundle/ChillMainBundle/Controller/UserController.php +++ b/src/Bundle/ChillMainBundle/Controller/UserController.php @@ -214,7 +214,7 @@ class UserController extends CRUDController return $this->redirect( $request->query->has('returnPath') ? $request->query->get('returnPath') : - $this->generateUrl('chill_main_homepage') + $this->generateUrl('chill_main_homepage') ); } @@ -250,7 +250,7 @@ class UserController extends CRUDController return $this->redirect( $request->query->has('returnPath') ? $request->query->get('returnPath') : - $this->generateUrl('chill_crud_admin_user_edit', ['id' => $user->getId()]) + $this->generateUrl('chill_crud_admin_user_edit', ['id' => $user->getId()]) ); } @@ -265,6 +265,7 @@ class UserController extends CRUDController return $this->getFilterOrderHelperFactory() ->create(self::class) ->addSearchBox(['label']) + ->addCheckbox('activeFilter', [true => 'Active', false => 'Inactive'], ['Active']) ->build(); } @@ -274,11 +275,7 @@ class UserController extends CRUDController return parent::countEntities($action, $request, $filterOrder); } - if (null === $filterOrder->getQueryString()) { - return parent::countEntities($action, $request, $filterOrder); - } - - return $this->userRepository->countByUsernameOrEmail($filterOrder->getQueryString()); + return $this->userRepository->countFilteredUsers($filterOrder->getQueryString(), $filterOrder->getCheckboxData('activeFilter')); } protected function createFormFor(string $action, $entity, ?string $formClass = null, array $formOptions = []): FormInterface @@ -335,16 +332,13 @@ class UserController extends CRUDController return parent::getQueryResult($action, $request, $totalItems, $paginator, $filterOrder); } - if (null === $filterOrder->getQueryString()) { - return parent::getQueryResult($action, $request, $totalItems, $paginator, $filterOrder); - } + $queryString = $filterOrder->getQueryString(); + $activeFilter = $filterOrder->getCheckboxData('activeFilter'); + $nb = $this->userRepository->countFilteredUsers($queryString, $activeFilter); - return $this->userRepository->findByUsernameOrEmail( - $filterOrder->getQueryString(), - ['usernameCanonical' => 'ASC'], - $paginator->getItemsPerPage(), - $paginator->getCurrentPageFirstItemNumber() - ); + $paginator = $this->getPaginatorFactory()->create($nb); + + return $this->userRepository->findFilteredUsers($queryString, $activeFilter, $paginator->getCurrentPageFirstItemNumber(), $paginator->getItemsPerPage()); } protected function onPrePersist(string $action, $entity, FormInterface $form, Request $request) @@ -375,10 +369,12 @@ class UserController extends CRUDController $returnPathParams = $request->query->has('returnPath') ? ['returnPath' => $request->query->get('returnPath')] : []; return $this->createFormBuilder() - ->setAction($this->generateUrl( - 'admin_user_add_groupcenter', - array_merge($returnPathParams, ['uid' => $user->getId()]) - )) + ->setAction( + $this->generateUrl( + 'admin_user_add_groupcenter', + array_merge($returnPathParams, ['uid' => $user->getId()]) + ) + ) ->setMethod('POST') ->add(self::FORM_GROUP_CENTER_COMPOSED, ComposedGroupCenterType::class) ->add('submit', SubmitType::class, ['label' => 'Add a new groupCenter']) @@ -393,10 +389,12 @@ class UserController extends CRUDController $returnPathParams = $request->query->has('returnPath') ? ['returnPath' => $request->query->get('returnPath')] : []; return $this->createFormBuilder() - ->setAction($this->generateUrl( - 'admin_user_delete_groupcenter', - array_merge($returnPathParams, ['uid' => $user->getId(), 'gcid' => $groupCenter->getId()]) - )) + ->setAction( + $this->generateUrl( + 'admin_user_delete_groupcenter', + array_merge($returnPathParams, ['uid' => $user->getId(), 'gcid' => $groupCenter->getId()]) + ) + ) ->setMethod('DELETE') ->add('submit', SubmitType::class, ['label' => 'Delete']) ->getForm(); diff --git a/src/Bundle/ChillMainBundle/Repository/UserRepository.php b/src/Bundle/ChillMainBundle/Repository/UserRepository.php index 5c5dff4d0..1a2fb6c26 100644 --- a/src/Bundle/ChillMainBundle/Repository/UserRepository.php +++ b/src/Bundle/ChillMainBundle/Repository/UserRepository.php @@ -17,6 +17,7 @@ use Doctrine\DBAL\Connection; use Doctrine\DBAL\Exception; use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityRepository; +use Doctrine\ORM\NonUniqueResultException; use Doctrine\ORM\NoResultException; use Doctrine\ORM\Query\ResultSetMapping; use Doctrine\ORM\Query\ResultSetMappingBuilder; @@ -26,9 +27,25 @@ final readonly class UserRepository implements UserRepositoryInterface { private EntityRepository $repository; - private const FIELDS = ['id', 'email', 'enabled', 'civility_id', 'civility_abbreviation', 'civility_name', 'label', 'mainCenter_id', - 'mainCenter_name', 'mainScope_id', 'mainScope_name', 'userJob_id', 'userJob_name', 'currentLocation_id', 'currentLocation_name', - 'mainLocation_id', 'mainLocation_name']; + private const FIELDS = [ + 'id', + 'email', + 'enabled', + 'civility_id', + 'civility_abbreviation', + 'civility_name', + 'label', + 'mainCenter_id', + 'mainCenter_name', + 'mainScope_id', + 'mainScope_name', + 'userJob_id', + 'userJob_name', + 'currentLocation_id', + 'currentLocation_name', + 'mainLocation_id', + 'mainLocation_name', + ]; public function __construct(private EntityManagerInterface $entityManager, private Connection $connection) { @@ -296,6 +313,25 @@ final readonly class UserRepository implements UserRepositoryInterface 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'); @@ -312,4 +348,49 @@ final readonly class UserRepository implements UserRepositoryInterface 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); + } + } } diff --git a/src/Bundle/ChillMainBundle/Tests/Repository/UserRepositoryTest.php b/src/Bundle/ChillMainBundle/Tests/Repository/UserRepositoryTest.php new file mode 100644 index 000000000..88919aa15 --- /dev/null +++ b/src/Bundle/ChillMainBundle/Tests/Repository/UserRepositoryTest.php @@ -0,0 +1,55 @@ +getContainer()->get('doctrine.orm.entity_manager'); + $connection = $entityManager->getConnection(); + $this->userRepository = new UserRepository($entityManager, $connection); + } + + public function testCountFilteredUsers(): void + { + self::assertIsInt($this->userRepository->countFilteredUsers(null, ['Active'])); + self::assertIsInt($this->userRepository->countFilteredUsers(null, ['Active', 'Inactive'])); + self::assertIsInt($this->userRepository->countFilteredUsers(null, ['Inactive'])); + self::assertIsInt($this->userRepository->countFilteredUsers('center', ['Active'])); + self::assertIsInt($this->userRepository->countFilteredUsers('center', ['Active', 'Inactive'])); + self::assertIsInt($this->userRepository->countFilteredUsers('center', ['Inactive'])); + self::assertIsInt($this->userRepository->countFilteredUsers('center')); + } + + public function testFindByFilteredUsers(): void + { + self::assertIsArray($this->userRepository->findFilteredUsers(null, ['Active'])); + self::assertIsArray($this->userRepository->findFilteredUsers(null, ['Active', 'Inactive'])); + self::assertIsArray($this->userRepository->findFilteredUsers(null, ['Inactive'])); + self::assertIsArray($this->userRepository->findFilteredUsers('center', ['Active'])); + self::assertIsArray($this->userRepository->findFilteredUsers('center', ['Active', 'Inactive'])); + self::assertIsArray($this->userRepository->findFilteredUsers('center', ['Inactive'])); + self::assertIsArray($this->userRepository->findFilteredUsers('center')); + } +}