From f2b60570a9d2721183cd177f62e4c816ca2322c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Fromme?= Date: Fri, 21 Mar 2025 12:38:55 +0100 Subject: [PATCH] fix: properly filter by date range, improve filter handlers --- .../Common/TimelineFilterController.php | 6 +-- src/Form/TimelineFilterType.php | 47 ++++++++++--------- src/Model/TimelineFilterDto.php | 19 ++++++++ src/Service/Common/AbstractFilterHandler.php | 10 +++- .../Common/ApplicationFilterHandler.php | 11 +++-- .../Common/AssignmentFilterHandler.php | 11 +++-- src/Service/Common/DocumentFilterHandler.php | 11 +++-- src/Service/Common/FeedbackFilterHandler.php | 11 +++-- src/Service/Common/TeamerFilterHandler.php | 11 +++-- src/Service/Common/TimelineFilterHandler.php | 23 ++++----- .../assignment/timeline.html.twig | 4 ++ 11 files changed, 98 insertions(+), 66 deletions(-) diff --git a/src/Controller/Common/TimelineFilterController.php b/src/Controller/Common/TimelineFilterController.php index 1f4d6f3..e55210d 100644 --- a/src/Controller/Common/TimelineFilterController.php +++ b/src/Controller/Common/TimelineFilterController.php @@ -30,12 +30,8 @@ class TimelineFilterController extends AbstractController public function index(Request $request): Response { $formData = $this->filterHandler->getFilterSettings(); - $formOptions = [ - 'min_date' => $formData->getPeriod()->first()->toDateTimeImmutable(), - 'max_date' => $formData->getPeriod()->last()->toDateTimeImmutable(), - ]; - $form = $this->createForm(TimelineFilterType::class, $formData, $formOptions); + $form = $this->createForm(TimelineFilterType::class, $formData); $form->handleRequest($request); if ($form->isSubmitted() && $form->isValid()) { diff --git a/src/Form/TimelineFilterType.php b/src/Form/TimelineFilterType.php index 1e321d6..d3f7a6b 100644 --- a/src/Form/TimelineFilterType.php +++ b/src/Form/TimelineFilterType.php @@ -3,6 +3,7 @@ namespace App\Form; use App\Model\TimelineFilterDto; +use App\Service\Assignment\TimelineService; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\Extension\Core\Type\SubmitType; use Symfony\Component\Form\FormBuilderInterface; @@ -10,25 +11,24 @@ use Symfony\Component\OptionsResolver\OptionsResolver; class TimelineFilterType extends AbstractType { - private array $hotelChoices = []; - - public function __construct(private readonly array $destinations) - { - $destinations = $this->destinations; - sort($destinations); - foreach ($destinations as $item) { - $this->hotelChoices[$item] = $item; - } + public function __construct( + private readonly TimelineService $timelineService, + private readonly array $destinations + ) { } public function buildForm(FormBuilderInterface $builder, array $options): void { + $period = $this->timelineService->getActiveWindow(); + $periodFrom = $period->first()->toDateTimeImmutable(); + $periodTo = $period->last()->toDateTimeImmutable(); + $builder ->add('dateFrom', DatepickerType::class, [ 'label' => 'Zeitraum von', 'required' => false, - 'min_date' => $options['min_date'], - 'max_date' => $options['max_date'], + 'min_date' => $periodFrom, + 'max_date' => $periodTo, 'attr' => [ 'placeholder' => 'nicht filtern', ], @@ -36,8 +36,8 @@ class TimelineFilterType extends AbstractType ->add('dateTo', DatepickerType::class, [ 'label' => 'Zeitraum bis', 'required' => false, - 'min_date' => $options['min_date'], - 'max_date' => $options['max_date'], + 'min_date' => $periodFrom, + 'max_date' => $periodTo, 'attr' => [ 'placeholder' => 'nicht filtern', ], @@ -50,26 +50,27 @@ class TimelineFilterType extends AbstractType ]) ; + $destinations = $this->destinations; + $hotelChoices = []; + sort($destinations); + foreach ($destinations as $item) { + $hotelChoices[$item] = $item; + } + $builder ->add('hotels', MultiselectType::class, [ 'label' => 'Haus/Destination', 'required' => false, 'empty_label' => 'nicht filtern', - 'choices' => $this->hotelChoices, + 'choices' => $hotelChoices, ]) ; } public function configureOptions(OptionsResolver $resolver): void { - $resolver - ->setDefaults([ - 'data_class' => TimelineFilterDto::class, - 'min_date' => null, - 'max_date' => null, - ]) - ->setAllowedTypes('min_date', [\DateTimeImmutable::class, 'null']) - ->setAllowedTypes('max_date', [\DateTimeImmutable::class, 'null']) - ; + $resolver->setDefaults([ + 'data_class' => TimelineFilterDto::class, + ]); } } diff --git a/src/Model/TimelineFilterDto.php b/src/Model/TimelineFilterDto.php index 8774d45..700f890 100644 --- a/src/Model/TimelineFilterDto.php +++ b/src/Model/TimelineFilterDto.php @@ -6,8 +6,27 @@ use Carbon\CarbonPeriod; class TimelineFilterDto extends AssignmentFilterDto { + protected bool $includePast = true; protected ?CarbonPeriod $_period = null; + public function getDateFrom(): ?\DateTimeImmutable + { + if (null === $this->dateFrom) { + return $this->_period->first()->toDateTimeImmutable(); + } + + return $this->dateFrom; + } + + public function getDateTo(): ?\DateTimeImmutable + { + if (null === $this->dateTo) { + return $this->_period->last()->toDateTimeImmutable(); + } + + return $this->dateTo; + } + public function getPeriod(): ?CarbonPeriod { return $this->_period; diff --git a/src/Service/Common/AbstractFilterHandler.php b/src/Service/Common/AbstractFilterHandler.php index 9fe8a74..22ae01a 100644 --- a/src/Service/Common/AbstractFilterHandler.php +++ b/src/Service/Common/AbstractFilterHandler.php @@ -12,6 +12,7 @@ use Symfony\Component\HttpFoundation\Session\SessionInterface; abstract class AbstractFilterHandler { protected string $namespace = 'filter:default'; + protected string $modelClass = AbstractFilterDto::class; public function __construct( protected readonly RequestStack $requestStack, @@ -26,12 +27,17 @@ abstract class AbstractFilterHandler return $this; } + public function setModelClass(string $modelClass): static + { + $this->modelClass = $modelClass; + } + public function handleRequest(FormInterface $form): AbstractFilterDto { $filterDto = $form->getData(); if ($form->get('reset')->isClicked()) { - $filterDto = new AssignmentFilterDto(); + $filterDto = new $this->modelClass(); $this->resetFilterSettings(); } elseif ($form->get('apply')->isClicked()) { $this->saveFilterSettings($filterDto); @@ -57,4 +63,4 @@ abstract class AbstractFilterHandler { return $this->requestStack->getSession(); } -} \ No newline at end of file +} diff --git a/src/Service/Common/ApplicationFilterHandler.php b/src/Service/Common/ApplicationFilterHandler.php index 1212ced..f4ee179 100644 --- a/src/Service/Common/ApplicationFilterHandler.php +++ b/src/Service/Common/ApplicationFilterHandler.php @@ -9,14 +9,15 @@ use App\Model\ApplicationFilterDto; class ApplicationFilterHandler extends AbstractFilterHandler { protected string $namespace = 'filter:application'; + protected string $modelClass = ApplicationFilterDto::class; public function getFilterSettings(): ApplicationFilterDto { - if (null === $data = $this->getSession()->get($this->namespace)) { - return new ApplicationFilterDto(); - } + $filterDto = new $this->modelClass(); - $filterDto = new ApplicationFilterDto(); + if (null === $data = $this->getSession()->get($this->namespace)) { + return $filterDto; + } if (isset($data['id'])) { $filterDto->setId($data['id']); @@ -67,4 +68,4 @@ class ApplicationFilterHandler extends AbstractFilterHandler 'include_past' => $filterDto->isIncludePast(), ]); } -} \ No newline at end of file +} diff --git a/src/Service/Common/AssignmentFilterHandler.php b/src/Service/Common/AssignmentFilterHandler.php index e0d1c19..a6f961c 100644 --- a/src/Service/Common/AssignmentFilterHandler.php +++ b/src/Service/Common/AssignmentFilterHandler.php @@ -9,14 +9,15 @@ use App\Model\AssignmentFilterDto; class AssignmentFilterHandler extends AbstractFilterHandler { protected string $namespace = 'filter:assignment'; + protected string $modelClass = AssignmentFilterDto::class; public function getFilterSettings(): AssignmentFilterDto { - if (null === $data = $this->getSession()->get($this->namespace)) { - return new AssignmentFilterDto(); - } + $filterDto = new $this->modelClass(); - $filterDto = new AssignmentFilterDto(); + if (null === $data = $this->getSession()->get($this->namespace)) { + return $filterDto; + } if (isset($data['id'])) { $filterDto->setId($data['id']); @@ -67,4 +68,4 @@ class AssignmentFilterHandler extends AbstractFilterHandler 'include_past' => $filterDto->isIncludePast(), ]); } -} \ No newline at end of file +} diff --git a/src/Service/Common/DocumentFilterHandler.php b/src/Service/Common/DocumentFilterHandler.php index a811884..9edb2c0 100644 --- a/src/Service/Common/DocumentFilterHandler.php +++ b/src/Service/Common/DocumentFilterHandler.php @@ -10,14 +10,15 @@ use App\Model\DocumentFilterDto; class DocumentFilterHandler extends AbstractFilterHandler { protected string $namespace = 'filter:document'; + protected string $modelClass = DocumentFilterDto::class; public function getFilterSettings(): DocumentFilterDto { - if (null === $data = $this->getSession()->get($this->namespace)) { - return new DocumentFilterDto(); - } + $filterDto = new $this->modelClass(); - $filterDto = new DocumentFilterDto(); + if (null === $data = $this->getSession()->get($this->namespace)) { + return $filterDto; + } $filterDto ->setType($data['type']) @@ -69,4 +70,4 @@ class DocumentFilterHandler extends AbstractFilterHandler 'status' => $filterDto->getStatus(), ]); } -} \ No newline at end of file +} diff --git a/src/Service/Common/FeedbackFilterHandler.php b/src/Service/Common/FeedbackFilterHandler.php index 81fffcf..0996a45 100644 --- a/src/Service/Common/FeedbackFilterHandler.php +++ b/src/Service/Common/FeedbackFilterHandler.php @@ -8,14 +8,15 @@ use App\Model\FeedbackFilterDto; class FeedbackFilterHandler extends AbstractFilterHandler { protected string $namespace = 'filter:feedback'; + protected string $modelClass = FeedbackFilterDto::class; public function getFilterSettings(): FeedbackFilterDto { - if (null === $data = $this->getSession()->get($this->namespace)) { - return new FeedbackFilterDto(); - } + $filterDto = new $this->modelClass(); - $filterDto = new FeedbackFilterDto(); + if (null === $data = $this->getSession()->get($this->namespace)) { + return $filterDto; + } if (isset($data['name'])) { $filterDto->setName($data['name']); @@ -43,4 +44,4 @@ class FeedbackFilterHandler extends AbstractFilterHandler 'average_rating' => $filterDto->getAverageRating(), ]); } -} \ No newline at end of file +} diff --git a/src/Service/Common/TeamerFilterHandler.php b/src/Service/Common/TeamerFilterHandler.php index c76f410..48f2f61 100644 --- a/src/Service/Common/TeamerFilterHandler.php +++ b/src/Service/Common/TeamerFilterHandler.php @@ -9,14 +9,15 @@ use App\Model\TeamerFilterDto; class TeamerFilterHandler extends AbstractFilterHandler { protected string $namespace = 'filter:teamer'; + protected string $modelClass = TeamerFilterDto::class; public function getFilterSettings(): TeamerFilterDto { - if (null === $data = $this->getSession()->get($this->namespace)) { - return new TeamerFilterDto(); - } + $filterDto = new $this->modelClass(); - $filterDto = new TeamerFilterDto(); + if (null === $data = $this->getSession()->get($this->namespace)) { + return $filterDto; + } $filterDto ->setName($data['name']) @@ -54,4 +55,4 @@ class TeamerFilterHandler extends AbstractFilterHandler }, $filterDto->getJobProfiles()), ]); } -} \ No newline at end of file +} diff --git a/src/Service/Common/TimelineFilterHandler.php b/src/Service/Common/TimelineFilterHandler.php index c922753..f3373c7 100644 --- a/src/Service/Common/TimelineFilterHandler.php +++ b/src/Service/Common/TimelineFilterHandler.php @@ -9,6 +9,8 @@ use Carbon\CarbonPeriod; class TimelineFilterHandler extends AbstractFilterHandler { protected string $namespace = 'filter:timeline'; + protected string $modelClass = TimelineFilterDto::class; + protected ?CarbonPeriod $period = null; public function setPeriod(CarbonPeriod $period): void @@ -18,25 +20,24 @@ class TimelineFilterHandler extends AbstractFilterHandler public function getFilterSettings(): TimelineFilterDto { - if (null === $data = $this->getSession()->get($this->namespace)) { - $filterDto = new TimelineFilterDto(); - $filterDto->setPeriod($this->period); - - return $filterDto; - } - - $filterDto = new TimelineFilterDto(); - - // override dates from form in case outside of selectable period $periodFrom = $this->period->first()->toDateTimeImmutable(); $periodTo = $this->period->last()->toDateTimeImmutable(); + $filterDto = new $this->modelClass(); + $filterDto->setPeriod($this->period); + + if (null === $data = $this->getSession()->get($this->namespace)) { + return $filterDto; + } + if (isset($data['date_from'])) { $dateFrom = max($periodFrom, $data['date_from']); + $filterDto->setDateFrom($dateFrom); $periodFrom = $dateFrom; } if (isset($data['date_to'])) { - $dateTo = max($periodTo, $data['date_to']); + $dateTo = min($periodTo, $data['date_to']); + $filterDto->setDateTo($dateTo); $periodTo = $dateTo; } if (isset($data['hotels']) && 0 < count($data['hotels'])) { diff --git a/templates/administrative/assignment/timeline.html.twig b/templates/administrative/assignment/timeline.html.twig index 38aba31..c5434e8 100644 --- a/templates/administrative/assignment/timeline.html.twig +++ b/templates/administrative/assignment/timeline.html.twig @@ -104,6 +104,10 @@ {% endfor %} {% endfor %} + {% else %} + + Keine Ergebnisse + {% endfor %}