From 4b25970ce0a43e82531280940c6507da8f071b41 Mon Sep 17 00:00:00 2001 From: Julie Lenaerts Date: Wed, 5 Jul 2023 15:35:13 +0200 Subject: [PATCH 01/11] FEATURE [filter] start implementation of social action filter --- .../AccompanyingCourseWorkController.php | 38 ++++++++++++++++++- .../AccompanyingPeriodWorkRepository.php | 2 +- .../translations/messages.fr.yml | 3 ++ 3 files changed, 41 insertions(+), 2 deletions(-) diff --git a/src/Bundle/ChillPersonBundle/Controller/AccompanyingCourseWorkController.php b/src/Bundle/ChillPersonBundle/Controller/AccompanyingCourseWorkController.php index 8d15ca30f..75d0bae7c 100644 --- a/src/Bundle/ChillPersonBundle/Controller/AccompanyingCourseWorkController.php +++ b/src/Bundle/ChillPersonBundle/Controller/AccompanyingCourseWorkController.php @@ -11,9 +11,14 @@ declare(strict_types=1); namespace Chill\PersonBundle\Controller; +use Chill\MainBundle\Entity\UserJob; use Chill\MainBundle\Pagination\PaginatorFactory; +use Chill\MainBundle\Templating\Listing\FilterOrderHelper; +use Chill\MainBundle\Templating\TranslatableStringHelperInterface; use Chill\PersonBundle\Entity\AccompanyingPeriod; use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWork; +use Chill\PersonBundle\Entity\Person; +use Chill\PersonBundle\Entity\SocialWork\SocialAction; use Chill\PersonBundle\Repository\AccompanyingPeriod\AccompanyingPeriodWorkRepository; use Chill\PersonBundle\Security\Authorization\AccompanyingPeriodWorkVoter; use Psr\Log\LoggerInterface; @@ -38,18 +43,22 @@ class AccompanyingCourseWorkController extends AbstractController private AccompanyingPeriodWorkRepository $workRepository; + private TranslatableStringHelperInterface $translatableStringHelper; + public function __construct( TranslatorInterface $trans, SerializerInterface $serializer, AccompanyingPeriodWorkRepository $workRepository, PaginatorFactory $paginator, - LoggerInterface $chillLogger + LoggerInterface $chillLogger, + TranslatableStringHelperInterface $translatableStringHelper ) { $this->trans = $trans; $this->serializer = $serializer; $this->workRepository = $workRepository; $this->paginator = $paginator; $this->chillLogger = $chillLogger; + $this->translatableStringHelper = $translatableStringHelper; } /** @@ -162,11 +171,21 @@ class AccompanyingCourseWorkController extends AbstractController { $this->denyAccessUnlessGranted(AccompanyingPeriodWorkVoter::SEE, $period); + $filter = $this->buildFilterOrder($period); + + $filterData = [ + 'types' => $filter->getEntityChoiceData('typesFilter'), + 'before' => $filter->getDateRangeData('dateFilter')['to'], + 'after' => $filter->getDateRangeData('dateFilter')['from'], + 'user' => $filter->getUserPickerData('userFilter') + ]; + $totalItems = $this->workRepository->countByAccompanyingPeriod($period); $paginator = $this->paginator->create($totalItems); $works = $this->workRepository->findByAccompanyingPeriodOpenFirst( $period, + $filterData, $paginator->getItemsPerPage(), $paginator->getCurrentPageFirstItemNumber() ); @@ -210,4 +229,21 @@ class AccompanyingCourseWorkController extends AbstractController ->add('submit', SubmitType::class, ['label' => 'Delete']) ->getForm(); } + + private function buildFilterOrder($associatedPeriod): FilterOrderHelper + { + + $filterBuilder = $this->filterOrderHelperFactory->create(self::class); + $types = $this->workRepository->findByAccompanyingPeriod($associatedPeriod); + + $filterBuilder + ->addDateRange('dateFilter', 'accompanying_course_work.date_filter') + ->addEntityChoice('typesFilter', 'accompanying_course_work.types_filter', \Chill\PersonBundle\Entity\SocialAction::class, $types, [ + 'choice_label' => fn (SocialAction $sa) => $this->translatableStringHelper->localize($sa->getTitle()) + ]) + ->addUserPicker('userFilter', 'accompanying_course_work.user_filter') + ; + + return $filterBuilder->build(); + } } diff --git a/src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriod/AccompanyingPeriodWorkRepository.php b/src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriod/AccompanyingPeriodWorkRepository.php index bf2d34aae..1bd9b0f4e 100644 --- a/src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriod/AccompanyingPeriodWorkRepository.php +++ b/src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriod/AccompanyingPeriodWorkRepository.php @@ -96,7 +96,7 @@ final class AccompanyingPeriodWorkRepository implements ObjectRepository * * @return AccompanyingPeriodWork[] */ - public function findByAccompanyingPeriodOpenFirst(AccompanyingPeriod $period, int $limit = 10, int $offset = 0): array + public function findByAccompanyingPeriodOpenFirst(AccompanyingPeriod $period, $filters, int $limit = 10, int $offset = 0): array { $rsm = new ResultSetMappingBuilder($this->em); $rsm->addRootEntityFromClassMetadata(AccompanyingPeriodWork::class, 'w'); diff --git a/src/Bundle/ChillPersonBundle/translations/messages.fr.yml b/src/Bundle/ChillPersonBundle/translations/messages.fr.yml index 01383c050..f783c403e 100644 --- a/src/Bundle/ChillPersonBundle/translations/messages.fr.yml +++ b/src/Bundle/ChillPersonBundle/translations/messages.fr.yml @@ -912,6 +912,9 @@ accompanying_course_work: social_evaluation: Évaluation private_comment: Commentaire privé timeSpent: Temps de rédaction + date_filter: Filtrer par date + types_filter: Filtrer par type d'action + user_filter: Filtrer par intervenant # From 6c58e7eb3e13a3421250263f69043740787e0c8d Mon Sep 17 00:00:00 2001 From: Julie Lenaerts Date: Wed, 5 Jul 2023 15:49:50 +0200 Subject: [PATCH 02/11] DX phpstan and cs-fixer --- .../AccompanyingCourseWorkController.php | 34 +++++-------------- .../AccompanyingPeriodWorkRepository.php | 1 + 2 files changed, 10 insertions(+), 25 deletions(-) diff --git a/src/Bundle/ChillPersonBundle/Controller/AccompanyingCourseWorkController.php b/src/Bundle/ChillPersonBundle/Controller/AccompanyingCourseWorkController.php index 75d0bae7c..9abf1677d 100644 --- a/src/Bundle/ChillPersonBundle/Controller/AccompanyingCourseWorkController.php +++ b/src/Bundle/ChillPersonBundle/Controller/AccompanyingCourseWorkController.php @@ -14,6 +14,7 @@ namespace Chill\PersonBundle\Controller; use Chill\MainBundle\Entity\UserJob; use Chill\MainBundle\Pagination\PaginatorFactory; use Chill\MainBundle\Templating\Listing\FilterOrderHelper; +use Chill\MainBundle\Templating\Listing\FilterOrderHelperFactoryInterface; use Chill\MainBundle\Templating\TranslatableStringHelperInterface; use Chill\PersonBundle\Entity\AccompanyingPeriod; use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWork; @@ -33,32 +34,15 @@ use Symfony\Contracts\Translation\TranslatorInterface; class AccompanyingCourseWorkController extends AbstractController { - private LoggerInterface $chillLogger; - - private PaginatorFactory $paginator; - - private SerializerInterface $serializer; - - private TranslatorInterface $trans; - - private AccompanyingPeriodWorkRepository $workRepository; - - private TranslatableStringHelperInterface $translatableStringHelper; - public function __construct( - TranslatorInterface $trans, - SerializerInterface $serializer, - AccompanyingPeriodWorkRepository $workRepository, - PaginatorFactory $paginator, - LoggerInterface $chillLogger, - TranslatableStringHelperInterface $translatableStringHelper + private readonly TranslatorInterface $trans, + private readonly SerializerInterface $serializer, + private readonly AccompanyingPeriodWorkRepository $workRepository, + private readonly PaginatorFactory $paginator, + private readonly LoggerInterface $chillLogger, + private readonly TranslatableStringHelperInterface $translatableStringHelper, + private readonly FilterOrderHelperFactoryInterface $filterOrderHelperFactory ) { - $this->trans = $trans; - $this->serializer = $serializer; - $this->workRepository = $workRepository; - $this->paginator = $paginator; - $this->chillLogger = $chillLogger; - $this->translatableStringHelper = $translatableStringHelper; } /** @@ -238,7 +222,7 @@ class AccompanyingCourseWorkController extends AbstractController $filterBuilder ->addDateRange('dateFilter', 'accompanying_course_work.date_filter') - ->addEntityChoice('typesFilter', 'accompanying_course_work.types_filter', \Chill\PersonBundle\Entity\SocialAction::class, $types, [ + ->addEntityChoice('typesFilter', 'accompanying_course_work.types_filter', \Chill\PersonBundle\Entity\SocialWork\SocialAction::class, $types, [ 'choice_label' => fn (SocialAction $sa) => $this->translatableStringHelper->localize($sa->getTitle()) ]) ->addUserPicker('userFilter', 'accompanying_course_work.user_filter') diff --git a/src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriod/AccompanyingPeriodWorkRepository.php b/src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriod/AccompanyingPeriodWorkRepository.php index 1bd9b0f4e..60a00e12a 100644 --- a/src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriod/AccompanyingPeriodWorkRepository.php +++ b/src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriod/AccompanyingPeriodWorkRepository.php @@ -95,6 +95,7 @@ final class AccompanyingPeriodWorkRepository implements ObjectRepository * * then, closed works * * @return AccompanyingPeriodWork[] + * @param mixed $filters */ public function findByAccompanyingPeriodOpenFirst(AccompanyingPeriod $period, $filters, int $limit = 10, int $offset = 0): array { From 61982634a6203875f6bcaf0fd30fd454db204729 Mon Sep 17 00:00:00 2001 From: Julie Lenaerts Date: Wed, 5 Jul 2023 16:05:51 +0200 Subject: [PATCH 03/11] FEATURE add filter to the template --- .../Controller/AccompanyingCourseWorkController.php | 3 ++- .../Resources/views/AccompanyingCourseWork/index.html.twig | 5 +++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/Bundle/ChillPersonBundle/Controller/AccompanyingCourseWorkController.php b/src/Bundle/ChillPersonBundle/Controller/AccompanyingCourseWorkController.php index 9abf1677d..1f2680f9e 100644 --- a/src/Bundle/ChillPersonBundle/Controller/AccompanyingCourseWorkController.php +++ b/src/Bundle/ChillPersonBundle/Controller/AccompanyingCourseWorkController.php @@ -178,6 +178,7 @@ class AccompanyingCourseWorkController extends AbstractController 'accompanyingCourse' => $period, 'works' => $works, 'paginator' => $paginator, + 'filter' => $filter ]); } @@ -225,7 +226,7 @@ class AccompanyingCourseWorkController extends AbstractController ->addEntityChoice('typesFilter', 'accompanying_course_work.types_filter', \Chill\PersonBundle\Entity\SocialWork\SocialAction::class, $types, [ 'choice_label' => fn (SocialAction $sa) => $this->translatableStringHelper->localize($sa->getTitle()) ]) - ->addUserPicker('userFilter', 'accompanying_course_work.user_filter') + ->addUserPicker('userFilter', 'accompanying_course_work.user_filter', ['required' => false]) ; return $filterBuilder->build(); diff --git a/src/Bundle/ChillPersonBundle/Resources/views/AccompanyingCourseWork/index.html.twig b/src/Bundle/ChillPersonBundle/Resources/views/AccompanyingCourseWork/index.html.twig index ab1989f63..1cedfa694 100644 --- a/src/Bundle/ChillPersonBundle/Resources/views/AccompanyingCourseWork/index.html.twig +++ b/src/Bundle/ChillPersonBundle/Resources/views/AccompanyingCourseWork/index.html.twig @@ -5,18 +5,23 @@ {% block js %} {{ parent() }} {{ encore_entry_script_tags('mod_entity_workflow_pick') }} + {{ encore_entry_script_tags('mod_pickentity_type') }} {% endblock %} {% block css %} {{ parent() }} {{ encore_entry_link_tags('mod_entity_workflow_pick') }} + {{ encore_entry_link_tags('mod_pickentity_type') }} {% endblock %} + {% block content %}

{{ block('title') }}

+ {{ filter|chill_render_filter_order_helper }} + {% if works|length == 0 %}

{{ 'accompanying_course_work.Any work'|trans }}

{% else %} From 20d5fabc1876e65ca07ffb18b1cfa477ae1fc2c4 Mon Sep 17 00:00:00 2001 From: Julie Lenaerts Date: Thu, 6 Jul 2023 13:39:08 +0200 Subject: [PATCH 04/11] [repository][action filter] integrating filters in repository --- .../AccompanyingCourseWorkController.php | 4 +- .../AccompanyingPeriodWorkRepository.php | 58 ++++++++++++++++++- 2 files changed, 58 insertions(+), 4 deletions(-) diff --git a/src/Bundle/ChillPersonBundle/Controller/AccompanyingCourseWorkController.php b/src/Bundle/ChillPersonBundle/Controller/AccompanyingCourseWorkController.php index 1f2680f9e..c56489afd 100644 --- a/src/Bundle/ChillPersonBundle/Controller/AccompanyingCourseWorkController.php +++ b/src/Bundle/ChillPersonBundle/Controller/AccompanyingCourseWorkController.php @@ -161,7 +161,7 @@ class AccompanyingCourseWorkController extends AbstractController 'types' => $filter->getEntityChoiceData('typesFilter'), 'before' => $filter->getDateRangeData('dateFilter')['to'], 'after' => $filter->getDateRangeData('dateFilter')['from'], - 'user' => $filter->getUserPickerData('userFilter') + 'users' => $filter->getUserPickerData('userFilter') ]; $totalItems = $this->workRepository->countByAccompanyingPeriod($period); @@ -219,7 +219,7 @@ class AccompanyingCourseWorkController extends AbstractController { $filterBuilder = $this->filterOrderHelperFactory->create(self::class); - $types = $this->workRepository->findByAccompanyingPeriod($associatedPeriod); + $types = $this->workRepository->findActionTypeByPeriod($associatedPeriod); $filterBuilder ->addDateRange('dateFilter', 'accompanying_course_work.date_filter') diff --git a/src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriod/AccompanyingPeriodWorkRepository.php b/src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriod/AccompanyingPeriodWorkRepository.php index 60a00e12a..a5e27afd5 100644 --- a/src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriod/AccompanyingPeriodWorkRepository.php +++ b/src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriod/AccompanyingPeriodWorkRepository.php @@ -108,8 +108,32 @@ final class AccompanyingPeriodWorkRepository implements ObjectRepository CASE WHEN enddate IS NULL THEN '-infinity'::timestamp ELSE 'infinity'::timestamp END ASC, startdate DESC, enddate DESC, - id DESC - LIMIT :limit OFFSET :offset"; + id DESC"; + + // implement filters + + if([] !== ($filters['types'] ?? [])) + { + $sql .= "AND WHERE w.socialAction IN (:types)"; + } + + if([] !== ($filters['users'] ?? [])) + { + $sql .= "AND WHERE w.createdBy IN (:users)"; + + foreach ($filters['users'] as $key => $user) { + $sql .= "OR :user_" . $key . " IN w.referrers)"; + + $nq = $this->em->createNativeQuery($sql, $rsm) + ->setParameter(':user_' . $key); + } + + // ... to be continued + } + + // set limit and offset + + $sql .= " LIMIT :limit OFFSET :offset"; $nq = $this->em->createNativeQuery($sql, $rsm) ->setParameter('periodId', $period->getId(), Types::INTEGER) @@ -119,6 +143,36 @@ final class AccompanyingPeriodWorkRepository implements ObjectRepository return $nq->getResult(); } + /** + * Return a list of types of social actions associated to the accompanying period + * + * @return array + */ + public function findActionTypeByPeriod(AccompanyingPeriod $period): array + { + $in = $this->em->createQueryBuilder(); + $in + ->select('1') + ->from(AccompanyingPeriodWork::class, 'apw'); + + + $in->andWhere('apw.accompanyingPeriod = :period')->setParameter('period', $period); + + + // join between the embedded exist query and the main query + $in->andWhere('apw.socialAction = sa'); + + $qb = $this->em->createQueryBuilder()->setParameters($in->getParameters()); + $qb + ->select('sa') + ->from(SocialAction::class, 'sa') + ->where( + $qb->expr()->exists($in->getDQL()) + ); + + return $qb->getQuery()->getResult(); + } + public function findNearEndDateByUser(User $user, DateTimeImmutable $since, DateTimeImmutable $until, int $limit = 20, int $offset = 0): array { return $this->buildQueryNearEndDateByUser($user, $since, $until) From cc97199c5deceb7c98bca0af26c659a9b5ea149d Mon Sep 17 00:00:00 2001 From: Julie Lenaerts Date: Thu, 6 Jul 2023 13:40:25 +0200 Subject: [PATCH 05/11] DX added changie --- .changes/unreleased/Feature-20230706-134010.yaml | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changes/unreleased/Feature-20230706-134010.yaml diff --git a/.changes/unreleased/Feature-20230706-134010.yaml b/.changes/unreleased/Feature-20230706-134010.yaml new file mode 100644 index 000000000..73a0727fc --- /dev/null +++ b/.changes/unreleased/Feature-20230706-134010.yaml @@ -0,0 +1,5 @@ +kind: Feature +body: Adding OrderFilter to the list of social actions. +time: 2023-07-06T13:40:10.339001208+02:00 +custom: + Issue: "120" From e0758215ba3c557fc26065591663be2389e1fe50 Mon Sep 17 00:00:00 2001 From: Julie Lenaerts Date: Wed, 12 Jul 2023 15:17:03 +0200 Subject: [PATCH 06/11] FEATURE [repository] implement filter logic --- src/Bundle/ChillMainBundle/Entity/Address.php | 1 + .../Templating/Listing/FilterOrderHelper.php | 3 +- .../AccompanyingCourseWorkController.php | 4 +- .../AccompanyingPeriodWorkRepository.php | 42 ++++++++++++------- 4 files changed, 32 insertions(+), 18 deletions(-) diff --git a/src/Bundle/ChillMainBundle/Entity/Address.php b/src/Bundle/ChillMainBundle/Entity/Address.php index 1bd1a453a..9fcb07fe5 100644 --- a/src/Bundle/ChillMainBundle/Entity/Address.php +++ b/src/Bundle/ChillMainBundle/Entity/Address.php @@ -255,6 +255,7 @@ class Address implements TrackCreationInterface, TrackUpdateInterface public function syncWithReference(AddressReference $addressReference): Address { + dump($addressReference); $this ->setPoint($addressReference->getPoint()) ->setPostcode($addressReference->getPostcode()) diff --git a/src/Bundle/ChillMainBundle/Templating/Listing/FilterOrderHelper.php b/src/Bundle/ChillMainBundle/Templating/Listing/FilterOrderHelper.php index 8554b4431..6a4d07167 100644 --- a/src/Bundle/ChillMainBundle/Templating/Listing/FilterOrderHelper.php +++ b/src/Bundle/ChillMainBundle/Templating/Listing/FilterOrderHelper.php @@ -11,6 +11,7 @@ declare(strict_types=1); namespace Chill\MainBundle\Templating\Listing; +use Chill\MainBundle\Entity\User; use Chill\MainBundle\Form\Type\Listing\FilterOrderType; use DateTimeImmutable; use Symfony\Component\Form\Extension\Core\Type\FormType; @@ -134,7 +135,7 @@ class FilterOrderHelper return $this->userPickers; } - public function getUserPickerData(string $name): array + public function getUserPickerData(string $name) { return $this->getFormData()['user_pickers'][$name]; } diff --git a/src/Bundle/ChillPersonBundle/Controller/AccompanyingCourseWorkController.php b/src/Bundle/ChillPersonBundle/Controller/AccompanyingCourseWorkController.php index c56489afd..3eb6e67f5 100644 --- a/src/Bundle/ChillPersonBundle/Controller/AccompanyingCourseWorkController.php +++ b/src/Bundle/ChillPersonBundle/Controller/AccompanyingCourseWorkController.php @@ -161,7 +161,7 @@ class AccompanyingCourseWorkController extends AbstractController 'types' => $filter->getEntityChoiceData('typesFilter'), 'before' => $filter->getDateRangeData('dateFilter')['to'], 'after' => $filter->getDateRangeData('dateFilter')['from'], - 'users' => $filter->getUserPickerData('userFilter') + 'user' => $filter->getUserPickerData('userFilter') ]; $totalItems = $this->workRepository->countByAccompanyingPeriod($period); @@ -226,7 +226,7 @@ class AccompanyingCourseWorkController extends AbstractController ->addEntityChoice('typesFilter', 'accompanying_course_work.types_filter', \Chill\PersonBundle\Entity\SocialWork\SocialAction::class, $types, [ 'choice_label' => fn (SocialAction $sa) => $this->translatableStringHelper->localize($sa->getTitle()) ]) - ->addUserPicker('userFilter', 'accompanying_course_work.user_filter', ['required' => false]) + ->addUserPicker('userFilter', 'accompanying_course_work.user_filter', ['required' => false, 'multiple' => false]) ; return $filterBuilder->build(); diff --git a/src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriod/AccompanyingPeriodWorkRepository.php b/src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriod/AccompanyingPeriodWorkRepository.php index a5e27afd5..6fc8ff86e 100644 --- a/src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriod/AccompanyingPeriodWorkRepository.php +++ b/src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriod/AccompanyingPeriodWorkRepository.php @@ -103,40 +103,52 @@ final class AccompanyingPeriodWorkRepository implements ObjectRepository $rsm->addRootEntityFromClassMetadata(AccompanyingPeriodWork::class, 'w'); $sql = "SELECT {$rsm} FROM chill_person_accompanying_period_work w - WHERE accompanyingPeriod_id = :periodId - ORDER BY - CASE WHEN enddate IS NULL THEN '-infinity'::timestamp ELSE 'infinity'::timestamp END ASC, - startdate DESC, - enddate DESC, - id DESC"; + WHERE accompanyingPeriod_id = :periodId"; // implement filters if([] !== ($filters['types'] ?? [])) { - $sql .= "AND WHERE w.socialAction IN (:types)"; + $sql .= " AND w.socialaction_id IN (:types)"; } if([] !== ($filters['users'] ?? [])) { - $sql .= "AND WHERE w.createdBy IN (:users)"; + $sql .= " AND w.createdBy = (:userCreated)"; - foreach ($filters['users'] as $key => $user) { - $sql .= "OR :user_" . $key . " IN w.referrers)"; + $sql .= " OR :userReferrer IN (w.referrers)"; + } - $nq = $this->em->createNativeQuery($sql, $rsm) - ->setParameter(':user_' . $key); - } - - // ... to be continued + if (null !== ($after = $filters['after'] ?? null) && null === $filters['before']) { + $sql .= " AND w.startdate::date >= :after"; + } elseif (null !== ($before = $filters['before'] ?? null) && null === $filters['after']) { + $sql .= " AND COALESCE(w.enddate::date, 'infinity'::date) <= :before"; + } elseif (null !== ($after = $filters['after'] ?? null) && null !== ($before = $filters['before'] ?? null)) { + $sql .= " AND w.startdate::date >= :after AND COALESCE(w.enddate::date, 'now'::date) <= :before"; } // set limit and offset + $sql .= " ORDER BY + CASE WHEN enddate IS NULL THEN '-infinity'::timestamp ELSE 'infinity'::timestamp END ASC, + startdate DESC, + enddate DESC, + id DESC"; + $sql .= " LIMIT :limit OFFSET :offset"; + $typeIds = []; + foreach ($filters['types'] as $type) { + $typeIds[] = $type->getId(); + } + $nq = $this->em->createNativeQuery($sql, $rsm) ->setParameter('periodId', $period->getId(), Types::INTEGER) + ->setParameter('types', $typeIds) + ->setParameter('userCreated', $filters['user']) + ->setParameter('userReferrer', $filters['user']) + ->setParameter('after', $filters['after']) + ->setParameter('before', $filters['before']) ->setParameter('limit', $limit, Types::INTEGER) ->setParameter('offset', $offset, Types::INTEGER); From 6bc45bbca38683f830c53afe1fb34aaadeab478f Mon Sep 17 00:00:00 2001 From: Julie Lenaerts Date: Wed, 12 Jul 2023 20:21:14 +0200 Subject: [PATCH 07/11] FEATURE [filter][actions] integrate user and dates filter into sql --- .../AccompanyingPeriodWorkRepository.php | 26 +++++++------------ 1 file changed, 10 insertions(+), 16 deletions(-) diff --git a/src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriod/AccompanyingPeriodWorkRepository.php b/src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriod/AccompanyingPeriodWorkRepository.php index 6fc8ff86e..018cb3209 100644 --- a/src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriod/AccompanyingPeriodWorkRepository.php +++ b/src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriod/AccompanyingPeriodWorkRepository.php @@ -103,7 +103,8 @@ final class AccompanyingPeriodWorkRepository implements ObjectRepository $rsm->addRootEntityFromClassMetadata(AccompanyingPeriodWork::class, 'w'); $sql = "SELECT {$rsm} FROM chill_person_accompanying_period_work w - WHERE accompanyingPeriod_id = :periodId"; + LEFT JOIN chill_person_accompanying_period_work_referrer AS rw ON accompanyingperiodwork_id = w.id + WHERE accompanyingPeriod_id = :periodId"; // implement filters @@ -112,20 +113,12 @@ final class AccompanyingPeriodWorkRepository implements ObjectRepository $sql .= " AND w.socialaction_id IN (:types)"; } - if([] !== ($filters['users'] ?? [])) + if([] !== ($filters['user'] ?? [])) { - $sql .= " AND w.createdBy = (:userCreated)"; - - $sql .= " OR :userReferrer IN (w.referrers)"; + $sql .= " AND rw.user_id = :user"; } - if (null !== ($after = $filters['after'] ?? null) && null === $filters['before']) { - $sql .= " AND w.startdate::date >= :after"; - } elseif (null !== ($before = $filters['before'] ?? null) && null === $filters['after']) { - $sql .= " AND COALESCE(w.enddate::date, 'infinity'::date) <= :before"; - } elseif (null !== ($after = $filters['after'] ?? null) && null !== ($before = $filters['before'] ?? null)) { - $sql .= " AND w.startdate::date >= :after AND COALESCE(w.enddate::date, 'now'::date) <= :before"; - } + $sql .= " AND daterange(:after::date, :before::date) && daterange(w.startDate, w.endDate)"; // set limit and offset @@ -137,6 +130,8 @@ final class AccompanyingPeriodWorkRepository implements ObjectRepository $sql .= " LIMIT :limit OFFSET :offset"; + dump($sql); + $typeIds = []; foreach ($filters['types'] as $type) { $typeIds[] = $type->getId(); @@ -145,10 +140,9 @@ final class AccompanyingPeriodWorkRepository implements ObjectRepository $nq = $this->em->createNativeQuery($sql, $rsm) ->setParameter('periodId', $period->getId(), Types::INTEGER) ->setParameter('types', $typeIds) - ->setParameter('userCreated', $filters['user']) - ->setParameter('userReferrer', $filters['user']) - ->setParameter('after', $filters['after']) - ->setParameter('before', $filters['before']) + ->setParameter('user', $filters['user'] ? $filters['user']->getId() : null) + ->setParameter('after', ($filters['after'] ?? null)) + ->setParameter('before', ($filters['before'] ?? null)) ->setParameter('limit', $limit, Types::INTEGER) ->setParameter('offset', $offset, Types::INTEGER); From 7bee376718bf614a730c414f5635ecf954ada453 Mon Sep 17 00:00:00 2001 From: Julie Lenaerts Date: Wed, 12 Jul 2023 20:29:53 +0200 Subject: [PATCH 08/11] DX phpstan fix and remove dump --- .../AccompanyingPeriod/AccompanyingPeriodWorkRepository.php | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriod/AccompanyingPeriodWorkRepository.php b/src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriod/AccompanyingPeriodWorkRepository.php index 018cb3209..188694a49 100644 --- a/src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriod/AccompanyingPeriodWorkRepository.php +++ b/src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriod/AccompanyingPeriodWorkRepository.php @@ -130,8 +130,6 @@ final class AccompanyingPeriodWorkRepository implements ObjectRepository $sql .= " LIMIT :limit OFFSET :offset"; - dump($sql); - $typeIds = []; foreach ($filters['types'] as $type) { $typeIds[] = $type->getId(); @@ -140,7 +138,7 @@ final class AccompanyingPeriodWorkRepository implements ObjectRepository $nq = $this->em->createNativeQuery($sql, $rsm) ->setParameter('periodId', $period->getId(), Types::INTEGER) ->setParameter('types', $typeIds) - ->setParameter('user', $filters['user'] ? $filters['user']->getId() : null) + ->setParameter('user', null !== $filters['user'] ? $filters['user']->getId() : null) ->setParameter('after', ($filters['after'] ?? null)) ->setParameter('before', ($filters['before'] ?? null)) ->setParameter('limit', $limit, Types::INTEGER) From 66e10477520f9ea60703680266e7088deb1318e4 Mon Sep 17 00:00:00 2001 From: Julie Lenaerts Date: Wed, 12 Jul 2023 20:30:31 +0200 Subject: [PATCH 09/11] DX php cs fixer --- .../AccompanyingPeriodWorkRepository.php | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriod/AccompanyingPeriodWorkRepository.php b/src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriod/AccompanyingPeriodWorkRepository.php index 188694a49..8c177f67c 100644 --- a/src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriod/AccompanyingPeriodWorkRepository.php +++ b/src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriod/AccompanyingPeriodWorkRepository.php @@ -108,13 +108,11 @@ final class AccompanyingPeriodWorkRepository implements ObjectRepository // implement filters - if([] !== ($filters['types'] ?? [])) - { + if([] !== ($filters['types'] ?? [])) { $sql .= " AND w.socialaction_id IN (:types)"; } - if([] !== ($filters['user'] ?? [])) - { + if([] !== ($filters['user'] ?? [])) { $sql .= " AND rw.user_id = :user"; } @@ -156,8 +154,8 @@ final class AccompanyingPeriodWorkRepository implements ObjectRepository { $in = $this->em->createQueryBuilder(); $in - ->select('1') - ->from(AccompanyingPeriodWork::class, 'apw'); + ->select('1') + ->from(AccompanyingPeriodWork::class, 'apw'); $in->andWhere('apw.accompanyingPeriod = :period')->setParameter('period', $period); From 18535ee85f54cc5d4572d218798940c964b6cf0a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Thu, 13 Jul 2023 21:12:08 +0200 Subject: [PATCH 10/11] DX: remove dump message --- src/Bundle/ChillMainBundle/Entity/Address.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Bundle/ChillMainBundle/Entity/Address.php b/src/Bundle/ChillMainBundle/Entity/Address.php index 9fcb07fe5..1bd1a453a 100644 --- a/src/Bundle/ChillMainBundle/Entity/Address.php +++ b/src/Bundle/ChillMainBundle/Entity/Address.php @@ -255,7 +255,6 @@ class Address implements TrackCreationInterface, TrackUpdateInterface public function syncWithReference(AddressReference $addressReference): Address { - dump($addressReference); $this ->setPoint($addressReference->getPoint()) ->setPostcode($addressReference->getPostcode()) From a21cefab31f5d1e8a6ea0628604f26cce8cdd1cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Fri, 14 Jul 2023 09:23:23 +0200 Subject: [PATCH 11/11] Fix: last fixes for accompanying period work filters - avoid errors when the user inverse date from and date to (we correct them) - allow the user to filter by multiple users - do not show filter by types if only one action type - more type-hinting in the $filter argument for AccompanyingPeriodWorkRepository::findByAccompanyingPeriodOpenFirst --- .../Templating/Listing/FilterOrderHelper.php | 10 +----- .../AccompanyingCourseWorkController.php | 24 ++++++++----- .../AccompanyingPeriodWorkRepository.php | 35 ++++++++++++++----- 3 files changed, 42 insertions(+), 27 deletions(-) diff --git a/src/Bundle/ChillMainBundle/Templating/Listing/FilterOrderHelper.php b/src/Bundle/ChillMainBundle/Templating/Listing/FilterOrderHelper.php index 5c5d164fc..8bcc7ffae 100644 --- a/src/Bundle/ChillMainBundle/Templating/Listing/FilterOrderHelper.php +++ b/src/Bundle/ChillMainBundle/Templating/Listing/FilterOrderHelper.php @@ -14,21 +14,13 @@ namespace Chill\MainBundle\Templating\Listing; use Chill\MainBundle\Entity\User; use Chill\MainBundle\Form\Type\Listing\FilterOrderType; use DateTimeImmutable; -use Symfony\Component\Form\Extension\Core\Type\FormType; -use Symfony\Component\Form\Extension\Core\Type\HiddenType; use Symfony\Component\Form\FormFactoryInterface; use Symfony\Component\Form\FormInterface; use Symfony\Component\HttpFoundation\RequestStack; -use Symfony\Component\PropertyAccess\PropertyAccessor; -use Symfony\Component\PropertyAccess\PropertyAccessorInterface; -use Symfony\Component\PropertyAccess\PropertyPath; -use Symfony\Component\PropertyAccess\PropertyPathInterface; -use Symfony\Contracts\Translation\TranslatorInterface; use function array_merge; -use function count; -class FilterOrderHelper +final class FilterOrderHelper { private array $checkboxes = []; diff --git a/src/Bundle/ChillPersonBundle/Controller/AccompanyingCourseWorkController.php b/src/Bundle/ChillPersonBundle/Controller/AccompanyingCourseWorkController.php index 3eb6e67f5..66d020ad1 100644 --- a/src/Bundle/ChillPersonBundle/Controller/AccompanyingCourseWorkController.php +++ b/src/Bundle/ChillPersonBundle/Controller/AccompanyingCourseWorkController.php @@ -26,13 +26,14 @@ use Psr\Log\LoggerInterface; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\Form\Extension\Core\Type\SubmitType; use Symfony\Component\Form\Form; +use Symfony\Component\Form\FormInterface; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Annotation\Route; use Symfony\Component\Serializer\SerializerInterface; use Symfony\Contracts\Translation\TranslatorInterface; -class AccompanyingCourseWorkController extends AbstractController +final class AccompanyingCourseWorkController extends AbstractController { public function __construct( private readonly TranslatorInterface $trans, @@ -158,7 +159,7 @@ class AccompanyingCourseWorkController extends AbstractController $filter = $this->buildFilterOrder($period); $filterData = [ - 'types' => $filter->getEntityChoiceData('typesFilter'), + 'types' => $filter->hasEntityChoice('typesFilter') ? $filter->getEntityChoiceData('typesFilter') : [], 'before' => $filter->getDateRangeData('dateFilter')['to'], 'after' => $filter->getDateRangeData('dateFilter')['from'], 'user' => $filter->getUserPickerData('userFilter') @@ -203,7 +204,7 @@ class AccompanyingCourseWorkController extends AbstractController ]); } - private function createDeleteForm(int $id): Form + private function createDeleteForm(int $id): FormInterface { $params = []; $params['id'] = $id; @@ -217,16 +218,21 @@ class AccompanyingCourseWorkController extends AbstractController private function buildFilterOrder($associatedPeriod): FilterOrderHelper { - $filterBuilder = $this->filterOrderHelperFactory->create(self::class); $types = $this->workRepository->findActionTypeByPeriod($associatedPeriod); $filterBuilder - ->addDateRange('dateFilter', 'accompanying_course_work.date_filter') - ->addEntityChoice('typesFilter', 'accompanying_course_work.types_filter', \Chill\PersonBundle\Entity\SocialWork\SocialAction::class, $types, [ - 'choice_label' => fn (SocialAction $sa) => $this->translatableStringHelper->localize($sa->getTitle()) - ]) - ->addUserPicker('userFilter', 'accompanying_course_work.user_filter', ['required' => false, 'multiple' => false]) + ->addDateRange('dateFilter', 'accompanying_course_work.date_filter'); + + if (1 < count($types)) { + $filterBuilder + ->addEntityChoice('typesFilter', 'accompanying_course_work.types_filter', \Chill\PersonBundle\Entity\SocialWork\SocialAction::class, $types, [ + 'choice_label' => fn (SocialAction $sa) => $this->translatableStringHelper->localize($sa->getTitle()) + ]); + } + + $filterBuilder + ->addUserPicker('userFilter', 'accompanying_course_work.user_filter', ['required' => false]) ; return $filterBuilder->build(); diff --git a/src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriod/AccompanyingPeriodWorkRepository.php b/src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriod/AccompanyingPeriodWorkRepository.php index 8c177f67c..56365452a 100644 --- a/src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriod/AccompanyingPeriodWorkRepository.php +++ b/src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriod/AccompanyingPeriodWorkRepository.php @@ -95,9 +95,9 @@ final class AccompanyingPeriodWorkRepository implements ObjectRepository * * then, closed works * * @return AccompanyingPeriodWork[] - * @param mixed $filters + * @param array{types?: list, user?: list, after?: null|\DateTimeImmutable, before?: null|\DateTimeImmutable} $filters */ - public function findByAccompanyingPeriodOpenFirst(AccompanyingPeriod $period, $filters, int $limit = 10, int $offset = 0): array + public function findByAccompanyingPeriodOpenFirst(AccompanyingPeriod $period, array $filters, int $limit = 10, int $offset = 0): array { $rsm = new ResultSetMappingBuilder($this->em); $rsm->addRootEntityFromClassMetadata(AccompanyingPeriodWork::class, 'w'); @@ -108,18 +108,32 @@ final class AccompanyingPeriodWorkRepository implements ObjectRepository // implement filters - if([] !== ($filters['types'] ?? [])) { + if ([] !== ($filters['types'] ?? [])) { $sql .= " AND w.socialaction_id IN (:types)"; } - if([] !== ($filters['user'] ?? [])) { - $sql .= " AND rw.user_id = :user"; + if ([] !== ($filters['user'] ?? [])) { + $sql .= " AND rw.user_id IN (" + . implode( + ', ', + // we add a user_xx for each key of the 'user' list + array_map(fn (User $u, int $idx) => ':user_' . $idx, $filters['user'], array_keys($filters['user'])) + ) + . ")"; } $sql .= " AND daterange(:after::date, :before::date) && daterange(w.startDate, w.endDate)"; - // set limit and offset + // if the start and end date were inversed, we inverse the order to avoid an error + if (null !== ($filters['after'] ?? null) && null !== ($filters['before']) && $filters['after'] > $filters['before']) { + $before = $filters['after']; + $after = $filters['before']; + } else { + $before = $filters['before']; + $after = $filters['after']; + } + // set limit and offset $sql .= " ORDER BY CASE WHEN enddate IS NULL THEN '-infinity'::timestamp ELSE 'infinity'::timestamp END ASC, startdate DESC, @@ -136,12 +150,15 @@ final class AccompanyingPeriodWorkRepository implements ObjectRepository $nq = $this->em->createNativeQuery($sql, $rsm) ->setParameter('periodId', $period->getId(), Types::INTEGER) ->setParameter('types', $typeIds) - ->setParameter('user', null !== $filters['user'] ? $filters['user']->getId() : null) - ->setParameter('after', ($filters['after'] ?? null)) - ->setParameter('before', ($filters['before'] ?? null)) + ->setParameter('after', $after) + ->setParameter('before', $before) ->setParameter('limit', $limit, Types::INTEGER) ->setParameter('offset', $offset, Types::INTEGER); + foreach ($filters['user'] as $key => $user) { + $nq->setParameter('user_' . $key, $user); + } + return $nq->getResult(); }