From be448c650e9ded59c4e59f0fd45e713089b65c02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Mon, 26 May 2025 14:16:09 +0200 Subject: [PATCH] Refactor SavedExport listing to support filtering. Introduced filtering capabilities for SavedExport listings by title and description. Moved index functionality to a new `SavedExportIndexController` and updated the repository with the necessary filter logic. Adjusted the Twig template to render the new filter interface. --- .../Controller/SavedExportController.php | 55 --------- .../Controller/SavedExportIndexController.php | 104 ++++++++++++++++++ .../Repository/SavedExportRepository.php | 24 +++- .../SavedExportRepositoryInterface.php | 12 +- .../views/SavedExport/index.html.twig | 2 + 5 files changed, 140 insertions(+), 57 deletions(-) create mode 100644 src/Bundle/ChillMainBundle/Controller/SavedExportIndexController.php diff --git a/src/Bundle/ChillMainBundle/Controller/SavedExportController.php b/src/Bundle/ChillMainBundle/Controller/SavedExportController.php index 22e4a6ba7..ddda96e19 100644 --- a/src/Bundle/ChillMainBundle/Controller/SavedExportController.php +++ b/src/Bundle/ChillMainBundle/Controller/SavedExportController.php @@ -14,13 +14,8 @@ namespace Chill\MainBundle\Controller; use Chill\MainBundle\Entity\ExportGeneration; use Chill\MainBundle\Entity\SavedExport; use Chill\MainBundle\Entity\User; -use Chill\MainBundle\Export\ExportInterface; use Chill\MainBundle\Export\ExportManager; -use Chill\MainBundle\Export\GroupedExportInterface; use Chill\MainBundle\Form\SavedExportType; -use Chill\MainBundle\Repository\ExportGenerationRepository; -use Chill\MainBundle\Repository\SavedExportRepositoryInterface; -use Chill\MainBundle\Security\Authorization\ChillExportVoter; use Chill\MainBundle\Security\Authorization\ExportGenerationVoter; use Chill\MainBundle\Security\Authorization\SavedExportVoter; use Doctrine\ORM\EntityManagerInterface; @@ -46,11 +41,9 @@ final readonly class SavedExportController private EntityManagerInterface $entityManager, private ExportManager $exportManager, private FormFactoryInterface $formFactory, - private SavedExportRepositoryInterface $savedExportRepository, private Security $security, private TranslatorInterface $translator, private UrlGeneratorInterface $urlGenerator, - private ExportGenerationRepository $exportGenerationRepository, ) {} #[Route(path: '/{_locale}/exports/saved/{id}/delete', name: 'chill_main_export_saved_delete')] @@ -234,52 +227,4 @@ final readonly class SavedExportController $this->urlGenerator->generate('chill_main_export_saved_edit', ['id' => $savedExport->getId()]), ); } - - #[Route(path: '/{_locale}/exports/saved/my', name: 'chill_main_export_saved_list_my')] - public function list(): Response - { - $user = $this->security->getUser(); - - if (!$this->security->isGranted(ChillExportVoter::GENERATE_SAVED_EXPORT) || !$user instanceof User) { - throw new AccessDeniedHttpException(sprintf('Missing role: %s', ChillExportVoter::GENERATE_SAVED_EXPORT)); - } - - $exports = array_filter( - $this->savedExportRepository->findSharedWithUser($user, ['exportAlias' => 'ASC', 'title' => 'ASC']), - fn (SavedExport $savedExport): bool => $this->security->isGranted(SavedExportVoter::GENERATE, $savedExport), - ); - - // group by center - /** @var array $exportsGrouped */ - $exportsGrouped = []; - - foreach ($exports as $savedExport) { - $export = $this->exportManager->getExport($savedExport->getExportAlias()); - - $exportsGrouped[ - $export instanceof GroupedExportInterface - ? $this->translator->trans($export->getGroup()) : '_' - ][] = ['saved' => $savedExport, 'export' => $export]; - } - - ksort($exportsGrouped); - - // get last executions - $lastExecutions = []; - foreach ($exports as $savedExport) { - $lastExecutions[$savedExport->getId()->toString()] = $this->exportGenerationRepository - ->findExportGenerationBySavedExportAndUser($savedExport, $user, 5); - } - - return new Response( - $this->templating->render( - '@ChillMain/SavedExport/index.html.twig', - [ - 'grouped_exports' => $exportsGrouped, - 'total' => \count($exports), - 'last_executions' => $lastExecutions, - ], - ), - ); - } } diff --git a/src/Bundle/ChillMainBundle/Controller/SavedExportIndexController.php b/src/Bundle/ChillMainBundle/Controller/SavedExportIndexController.php new file mode 100644 index 000000000..82e9913da --- /dev/null +++ b/src/Bundle/ChillMainBundle/Controller/SavedExportIndexController.php @@ -0,0 +1,104 @@ +security->getUser(); + + if (!$this->security->isGranted(ChillExportVoter::GENERATE_SAVED_EXPORT) || !$user instanceof User) { + throw new AccessDeniedHttpException(sprintf('Missing role: %s', ChillExportVoter::GENERATE_SAVED_EXPORT)); + } + + $filter = $this->buildFilter(); + + $filterParams = []; + if ('' !== $filter->getQueryString() && null !== $filter->getQueryString()) { + $filterParams[SavedExportRepositoryInterface::FILTER_DESCRIPTION | SavedExportRepositoryInterface::FILTER_TITLE] = $filter->getQueryString(); + } + + $exports = array_filter( + $this->savedExportRepository->findSharedWithUser($user, ['exportAlias' => 'ASC', 'title' => 'ASC'], filters: $filterParams), + fn (SavedExport $savedExport): bool => $this->security->isGranted(SavedExportVoter::GENERATE, $savedExport), + ); + + // group by center + /** @var array $exportsGrouped */ + $exportsGrouped = []; + + foreach ($exports as $savedExport) { + $export = $this->exportManager->getExport($savedExport->getExportAlias()); + + $exportsGrouped[$export instanceof GroupedExportInterface + ? $this->translator->trans($export->getGroup()) : '_'][] = ['saved' => $savedExport, 'export' => $export]; + } + + ksort($exportsGrouped); + + // get last executions + $lastExecutions = []; + foreach ($exports as $savedExport) { + $lastExecutions[$savedExport->getId()->toString()] = $this->exportGenerationRepository + ->findExportGenerationBySavedExportAndUser($savedExport, $user, 5); + } + + return new Response( + $this->templating->render( + '@ChillMain/SavedExport/index.html.twig', + [ + 'grouped_exports' => $exportsGrouped, + 'total' => \count($exports), + 'last_executions' => $lastExecutions, + 'filter' => $filter, + ], + ), + ); + } + + private function buildFilter(): FilterOrderHelper + { + $filter = $this->filterOrderHelperFactory->create('saved-export-index-filter'); + $filter->addSearchBox(); + + return $filter->build(); + } +} diff --git a/src/Bundle/ChillMainBundle/Repository/SavedExportRepository.php b/src/Bundle/ChillMainBundle/Repository/SavedExportRepository.php index 63b68bd5d..9b0d5a2ef 100644 --- a/src/Bundle/ChillMainBundle/Repository/SavedExportRepository.php +++ b/src/Bundle/ChillMainBundle/Repository/SavedExportRepository.php @@ -18,6 +18,7 @@ use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityRepository; use Doctrine\ORM\QueryBuilder; use Doctrine\Persistence\ObjectRepository; +use Symfony\Component\String\UnicodeString; /** * @implements ObjectRepository @@ -60,7 +61,7 @@ class SavedExportRepository implements SavedExportRepositoryInterface return $this->prepareResult($qb, $orderBy, $limit, $offset); } - public function findSharedWithUser(User $user, ?array $orderBy = [], ?int $limit = null, ?int $offset = null): array + public function findSharedWithUser(User $user, ?array $orderBy = [], ?int $limit = null, ?int $offset = null, array $filters = []): array { $qb = $this->repository->createQueryBuilder('se'); @@ -76,6 +77,27 @@ class SavedExportRepository implements SavedExportRepositoryInterface ) ->setParameter('user', $user); + foreach ($filters as $key => $filter) { + if (self::FILTER_TITLE === ($key & self::FILTER_TITLE) + || self::FILTER_DESCRIPTION === ($key & self::FILTER_DESCRIPTION)) { + $filter = new UnicodeString($filter); + + $i = 0; + foreach ($filter->split(' ') as $word) { + $orx = $qb->expr()->orX(); + if (self::FILTER_TITLE === ($key & self::FILTER_TITLE)) { + $orx->add($qb->expr()->like('LOWER(se.title)', 'LOWER(:qs'.$i.')')); + } + if (self::FILTER_DESCRIPTION === ($key & self::FILTER_DESCRIPTION)) { + $orx->add($qb->expr()->like('LOWER(se.description)', 'LOWER(:qs'.$i.')')); + } + $qb->andWhere($orx); + $qb->setParameter('qs'.$i, '%'.$word->trim().'%'); + ++$i; + } + } + } + return $this->prepareResult($qb, $orderBy, $limit, $offset); } diff --git a/src/Bundle/ChillMainBundle/Repository/SavedExportRepositoryInterface.php b/src/Bundle/ChillMainBundle/Repository/SavedExportRepositoryInterface.php index d9bc1d42e..c62e0f29c 100644 --- a/src/Bundle/ChillMainBundle/Repository/SavedExportRepositoryInterface.php +++ b/src/Bundle/ChillMainBundle/Repository/SavedExportRepositoryInterface.php @@ -20,6 +20,9 @@ use Doctrine\Persistence\ObjectRepository; */ interface SavedExportRepositoryInterface extends ObjectRepository { + public const FILTER_TITLE = 0x01; + public const FILTER_DESCRIPTION = 0x10; + public function find($id): ?SavedExport; /** @@ -34,7 +37,14 @@ interface SavedExportRepositoryInterface extends ObjectRepository */ public function findByUser(User $user, ?array $orderBy = [], ?int $limit = null, ?int $offset = null): array; - public function findSharedWithUser(User $user, ?array $orderBy = [], ?int $limit = null, ?int $offset = null): array; + /** + * Get the saved export created by and the user and the ones shared with the user. + * + * @param array $filters filters where keys are one of the constant starting with FILTER_ + * + * @return list + */ + public function findSharedWithUser(User $user, ?array $orderBy = [], ?int $limit = null, ?int $offset = null, array $filters = []): array; public function findOneBy(array $criteria): ?SavedExport; diff --git a/src/Bundle/ChillMainBundle/Resources/views/SavedExport/index.html.twig b/src/Bundle/ChillMainBundle/Resources/views/SavedExport/index.html.twig index 40257a996..49bfda9e8 100644 --- a/src/Bundle/ChillMainBundle/Resources/views/SavedExport/index.html.twig +++ b/src/Bundle/ChillMainBundle/Resources/views/SavedExport/index.html.twig @@ -18,6 +18,7 @@ {% block title %}{{ 'saved_export.Saved exports'|trans }}{% endblock %} + {% macro render_export_card(saved, export, export_alias, generations) %}
@@ -88,6 +89,7 @@ {{ include('@ChillMain/Export/_navbar.html.twig', {'current' : 'my'}) }}
+ {{ filter|chill_render_filter_order_helper }} {% if total == 0 %}

{{ 'saved_export.Any saved export'|trans }}