From d450f06286f320b1869c6e2084607aa8fb4a82a1 Mon Sep 17 00:00:00 2001 From: Julie Lenaerts Date: Wed, 2 Nov 2022 20:02:46 +0100 Subject: [PATCH] filters and aggregator aside activity --- .../Aggregator/ByActivityTypeAggregator.php | 32 ++++++- .../src/Export/Export/CountAsideActivity.php | 6 +- .../Export/Filter/ByActivityTypeFilter.php | 57 ++++++++++-- .../src/Export/Filter/ByDateFilter.php | 92 +++++++++++++++++-- .../AsideActivityCategoryRepository.php | 5 + .../src/config/services/export.yaml | 18 ++++ .../src/translations/messages.fr.yml | 22 +++-- 7 files changed, 202 insertions(+), 30 deletions(-) diff --git a/src/Bundle/ChillAsideActivityBundle/src/Export/Aggregator/ByActivityTypeAggregator.php b/src/Bundle/ChillAsideActivityBundle/src/Export/Aggregator/ByActivityTypeAggregator.php index 488885bde..6c91c9336 100644 --- a/src/Bundle/ChillAsideActivityBundle/src/Export/Aggregator/ByActivityTypeAggregator.php +++ b/src/Bundle/ChillAsideActivityBundle/src/Export/Aggregator/ByActivityTypeAggregator.php @@ -12,12 +12,24 @@ declare(strict_types=1); namespace Chill\AsideActivityBundle\Export\Aggregator; use Chill\AsideActivityBundle\Export\Declarations; +use Chill\AsideActivityBundle\Repository\AsideActivityCategoryRepository; use Chill\MainBundle\Export\AggregatorInterface; +use Chill\MainBundle\Templating\TranslatableStringHelper; use Doctrine\ORM\QueryBuilder; use Symfony\Component\Form\FormBuilderInterface; class ByActivityTypeAggregator implements AggregatorInterface { + private AsideActivityCategoryRepository $asideActivityCategoryRepository; + + private TranslatableStringHelper $translatableStringHelper; + + public function __construct(AsideActivityCategoryRepository $asideActivityCategoryRepository, TranslatableStringHelper $translatableStringHelper) + { + $this->asideActivityCategoryRepository = $asideActivityCategoryRepository; + $this->translatableStringHelper = $translatableStringHelper; + } + public function addRole(): ?string { return null; @@ -25,8 +37,8 @@ class ByActivityTypeAggregator implements AggregatorInterface public function alterQuery(QueryBuilder $qb, $data) { - $qb->addSelect('AS aside_by_activity_type_aggregator') - ->addGroupBy('aside_by_activity_type_aggregator'); + $qb->addSelect('IDENTITY(aside.type) AS by_aside_activity_type_aggregator') + ->addGroupBy('by_aside_activity_type_aggregator'); } public function applyOn(): string @@ -41,20 +53,30 @@ class ByActivityTypeAggregator implements AggregatorInterface public function getLabels($key, array $values, $data) { - return static function ($value): string { + $this->asideActivityCategoryRepository->findBy(['id' => $values]); + + return function ($value): string { if ('_header' === $value) { + return 'export.aggregator.Aside activity type'; + } + + if (null === $value) { return ''; } + + $t = $this->asideActivityCategoryRepository->find($value); + + return $this->translatableStringHelper->localize($t->getTitle()); }; } public function getQueryKeys($data): array { - return ['aside_by_activity_type_aggregator']; + return ['by_aside_activity_type_aggregator']; } public function getTitle(): string { - return 'Group by aside activity type'; + return 'export.aggregator.Group by aside activity type'; } } diff --git a/src/Bundle/ChillAsideActivityBundle/src/Export/Export/CountAsideActivity.php b/src/Bundle/ChillAsideActivityBundle/src/Export/Export/CountAsideActivity.php index ebf90dc66..de747f85a 100644 --- a/src/Bundle/ChillAsideActivityBundle/src/Export/Export/CountAsideActivity.php +++ b/src/Bundle/ChillAsideActivityBundle/src/Export/Export/CountAsideActivity.php @@ -43,12 +43,12 @@ class CountAsideActivity implements ExportInterface, GroupedExportInterface public function getDescription(): string { - return 'Count aside activities by various parameters.'; + return 'export.Count aside activities by various parameters.'; } public function getGroup(): string { - return 'Exports of aside activities'; + return 'export.Exports of aside activities'; } public function getLabels($key, array $values, $data) @@ -77,7 +77,7 @@ class CountAsideActivity implements ExportInterface, GroupedExportInterface public function getTitle(): string { - return 'Count aside activities'; + return 'export.Count aside activities'; } public function getType(): string diff --git a/src/Bundle/ChillAsideActivityBundle/src/Export/Filter/ByActivityTypeFilter.php b/src/Bundle/ChillAsideActivityBundle/src/Export/Filter/ByActivityTypeFilter.php index 354bd9f3c..85b327795 100644 --- a/src/Bundle/ChillAsideActivityBundle/src/Export/Filter/ByActivityTypeFilter.php +++ b/src/Bundle/ChillAsideActivityBundle/src/Export/Filter/ByActivityTypeFilter.php @@ -11,13 +11,34 @@ declare(strict_types=1); namespace Chill\AsideActivityBundle\Export\Filter; +use Chill\AsideActivityBundle\Entity\AsideActivityCategory; use Chill\AsideActivityBundle\Export\Declarations; +use Chill\AsideActivityBundle\Repository\AsideActivityCategoryRepository; +use Chill\AsideActivityBundle\Templating\Entity\CategoryRender; use Chill\MainBundle\Export\FilterInterface; +use Chill\MainBundle\Templating\TranslatableStringHelperInterface; use Doctrine\ORM\QueryBuilder; +use Symfony\Bridge\Doctrine\Form\Type\EntityType; use Symfony\Component\Form\FormBuilderInterface; class ByActivityTypeFilter implements FilterInterface { + private AsideActivityCategoryRepository $asideActivityTypeRepository; + + private CategoryRender $categoryRender; + + private TranslatableStringHelperInterface $translatableStringHelper; + + public function __construct( + CategoryRender $categoryRender, + TranslatableStringHelperInterface $translatableStringHelper, + AsideActivityCategoryRepository $asideActivityTypeRepository + ) { + $this->categoryRender = $categoryRender; + $this->asideActivityTypeRepository = $asideActivityTypeRepository; + $this->translatableStringHelper = $translatableStringHelper; + } + public function addRole(): ?string { return null; @@ -25,11 +46,10 @@ class ByActivityTypeFilter implements FilterInterface public function alterQuery(QueryBuilder $qb, $data) { - $qb - ->andWhere( - $qb->expr()->in('', ':') - ) - ->setParameter('', $data[]); + $clause = $qb->expr()->in('aside.type', ':types'); + + $qb->andWhere($clause); + $qb->setParameter('types', $data['types']); } public function applyOn(): string @@ -39,17 +59,38 @@ class ByActivityTypeFilter implements FilterInterface public function buildForm(FormBuilderInterface $builder) { - //$builder->add(); + $builder + ->add('types', EntityType::class, [ + 'class' => AsideActivityCategory::class, + 'choices' => $this->asideActivityTypeRepository->findAllActive(), + 'required' => false, + 'multiple' => true, + 'expanded' => false, + 'attr' => [ + 'class' => 'select2', + ], + 'choice_label' => function (AsideActivityCategory $category) { + $options = []; + + return $this->categoryRender->renderString($category, $options); + }, + ]); } public function describeAction($data, $format = 'string'): array { - return ['', [ + $types = array_map( + fn (AsideActivityCategory $t): string => $this->translatableStringHelper->localize($t->getName()), + $this->asideActivityTypeRepository->findBy(['id' => $data['types']->toArray()]) + ); + + return ['export.filter.Filtered by aside activity type: only %type%', [ + '%type%' => implode(', ', $types), ]]; } public function getTitle(): string { - return 'Filter by aside activity type'; + return 'export.filter.Filter by aside activity type'; } } diff --git a/src/Bundle/ChillAsideActivityBundle/src/Export/Filter/ByDateFilter.php b/src/Bundle/ChillAsideActivityBundle/src/Export/Filter/ByDateFilter.php index 5a63e8e94..a7595281d 100644 --- a/src/Bundle/ChillAsideActivityBundle/src/Export/Filter/ByDateFilter.php +++ b/src/Bundle/ChillAsideActivityBundle/src/Export/Filter/ByDateFilter.php @@ -13,11 +13,26 @@ namespace Chill\AsideActivityBundle\Export\Filter; use Chill\AsideActivityBundle\Export\Declarations; use Chill\MainBundle\Export\FilterInterface; +use Chill\MainBundle\Form\Type\ChillDateType; +use Chill\MainBundle\Form\Type\Export\FilterType; +use DateTime; +use Doctrine\ORM\Query\Expr\Andx; use Doctrine\ORM\QueryBuilder; use Symfony\Component\Form\FormBuilderInterface; +use Symfony\Component\Form\FormError; +use Symfony\Component\Form\FormEvent; +use Symfony\Component\Form\FormEvents; +use Symfony\Contracts\Translation\TranslatorInterface; class ByDateFilter implements FilterInterface { + protected TranslatorInterface $translator; + + public function __construct(TranslatorInterface $translator) + { + $this->translator = $translator; + } + public function addRole(): ?string { return null; @@ -25,11 +40,22 @@ class ByDateFilter implements FilterInterface public function alterQuery(QueryBuilder $qb, $data) { - $qb - ->andWhere( - $qb->expr()->in('', ':') - ) - ->setParameter('', $data[]); + $where = $qb->getDQLPart('where'); + $clause = $qb->expr()->between( + 'aside.date', + ':date_from', + ':date_to' + ); + + if ($where instanceof Andx) { + $where->add($clause); + } else { + $where = $qb->expr()->andX($clause); + } + + $qb->add('where', $where); + $qb->setParameter('date_from', $data['date_from']); + $qb->setParameter('date_to', $data['date_to']); } public function applyOn(): string @@ -39,17 +65,67 @@ class ByDateFilter implements FilterInterface public function buildForm(FormBuilderInterface $builder) { - //$builder->add(); + $builder + ->add('date_from', ChillDateType::class, [ + 'label' => 'export.filter.Aside activities after this date', + 'data' => new DateTime(), + ]) + ->add('date_to', ChillDateType::class, [ + 'label' => 'export.filter.Aside activities before this date', + 'data' => new DateTime(), + ]); + + $builder->addEventListener(FormEvents::POST_SUBMIT, function (FormEvent $event) { + /** @var \Symfony\Component\Form\FormInterface $filterForm */ + $filterForm = $event->getForm()->getParent(); + $enabled = $filterForm->get(FilterType::ENABLED_FIELD)->getData(); + + if (true === $enabled) { + // if the filter is enabled, add some validation + $form = $event->getForm(); + $date_from = $form->get('date_from')->getData(); + $date_to = $form->get('date_to')->getData(); + + // check that fields are not empty + if (null === $date_from) { + $form->get('date_from')->addError(new FormError( + $this->translator->trans('This field ' + . 'should not be empty') + )); + } + + if (null === $date_to) { + $form->get('date_to')->addError(new FormError( + $this->translator->trans('This field ' + . 'should not be empty') + )); + } + + // check that date_from is before date_to + if ( + (null !== $date_from && null !== $date_to) + && $date_from >= $date_to + ) { + $form->get('date_to')->addError(new FormError( + $this->translator->trans('export.filter.This date should be after ' + . 'the date given in "Implied in an aside activity after ' + . 'this date" field') + )); + } + } + }); } public function describeAction($data, $format = 'string'): array { - return ['', [ + return ['export.filter.Filtered by aside activities between %dateFrom% and %dateTo%', [ + '%dateFrom%' => $data['date_from']->format('d-m-Y'), + '%dateTo%' => $data['date_to']->format('d-m-Y'), ]]; } public function getTitle(): string { - return 'Filter by aside activity date'; + return 'export.filter.Filter by aside activity date'; } } diff --git a/src/Bundle/ChillAsideActivityBundle/src/Repository/AsideActivityCategoryRepository.php b/src/Bundle/ChillAsideActivityBundle/src/Repository/AsideActivityCategoryRepository.php index 85ea9faf4..918cec586 100644 --- a/src/Bundle/ChillAsideActivityBundle/src/Repository/AsideActivityCategoryRepository.php +++ b/src/Bundle/ChillAsideActivityBundle/src/Repository/AsideActivityCategoryRepository.php @@ -38,6 +38,11 @@ class AsideActivityCategoryRepository implements ObjectRepository return $this->repository->findAll(); } + public function findAllActive(): array + { + return $this->repository->findBy(['isActive' => true]); + } + /** * @param mixed|null $limit * @param mixed|null $offset diff --git a/src/Bundle/ChillAsideActivityBundle/src/config/services/export.yaml b/src/Bundle/ChillAsideActivityBundle/src/config/services/export.yaml index 579791d41..1b6b05e1c 100644 --- a/src/Bundle/ChillAsideActivityBundle/src/config/services/export.yaml +++ b/src/Bundle/ChillAsideActivityBundle/src/config/services/export.yaml @@ -7,3 +7,21 @@ services: Chill\AsideActivityBundle\Export\Export\CountAsideActivity: tags: - { name: chill.export, alias: 'count_aside_activity' } + + ## Filters + chill.aside_activity.export.date_filter: + class: Chill\AsideActivityBundle\Export\Filter\ByDateFilter + tags: + - { name: chill.export_filter, alias: 'aside_activity_date_filter' } + + chill.aside_activity.export.type_filter: + class: Chill\AsideActivityBundle\Export\Filter\ByActivityTypeFilter + tags: + - { name: chill.export_filter, alias: 'aside_activity_type_filter' } + + ## Aggregators + + chill.aside_activity.export.type_aggregator: + class: Chill\AsideActivityBundle\Export\Aggregator\ByActivityTypeAggregator + tags: + - { name: chill.export_aggregator, alias: activity_type_aggregator } \ No newline at end of file diff --git a/src/Bundle/ChillAsideActivityBundle/src/translations/messages.fr.yml b/src/Bundle/ChillAsideActivityBundle/src/translations/messages.fr.yml index ed54fd488..35a4d6a22 100644 --- a/src/Bundle/ChillAsideActivityBundle/src/translations/messages.fr.yml +++ b/src/Bundle/ChillAsideActivityBundle/src/translations/messages.fr.yml @@ -168,9 +168,19 @@ Aside activity type configuration: Configuration des categories d'activités ann Aside activity configuration: Configuration des activités annexes # exports -Exports of aside activities: Exports des activités annexes -Count aside activities: Nombre d'activités annexes -Count aside activities by various parameters.: Compte le nombre d'activités annexes selon divers critères -Filter by aside activity date: Filtrer les activités annexes par date -Filter by aside activity type: Filtrer les activités annexes par type d'activité -Group by aside activity type: Grouper les activités annexes par type d'activité \ No newline at end of file +export: + Exports of aside activities: Exports des activités annexes + Count aside activities: Nombre d'activités annexes + Count aside activities by various parameters.: Compte le nombre d'activités annexes selon divers critères + filter: + Filter by aside activity date: Filtrer les activités annexes par date + Filter by aside activity type: Filtrer les activités annexes par type d'activité + 'Filtered by aside activity type: only %type%': "Filtré par type d'activité annexe: uniquement %type%" + This date should be after the date given in "Implied in an aside activity after this date" field: Cette date devrait être postérieure à la date donnée dans le champ "activités annexes après cette date" + Aside activities after this date: Actvitités annexes après cette date + Aside activities before this date: Actvitités annexes avant cette date + aggregator: + Group by aside activity type: Grouper les activités annexes par type d'activité + Aside activity type: Type d'activité annexe + +