feat: filter documents

This commit is contained in:
Björn Fromme
2024-08-27 12:38:19 +02:00
parent 5ac77e9e9c
commit bcaab582cc
12 changed files with 476 additions and 191 deletions
@@ -3,6 +3,7 @@
namespace App\Controller\Administrative\Document;
use App\Repository\UploadRepository;
use App\Service\Common\DocumentFilterHandler;
use Knp\Component\Pager\PaginatorInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
@@ -14,7 +15,8 @@ class IndexController extends AbstractController
{
public function __construct(
private readonly UploadRepository $uploadRepository,
private readonly PaginatorInterface $paginator
private readonly PaginatorInterface $paginator,
private readonly DocumentFilterHandler $filterHandler
) {
}
@@ -22,9 +24,11 @@ class IndexController extends AbstractController
#[IsGranted('ROLE_ADMINISTRATIVE')]
public function index(Request $request): Response
{
$filterDto = $this->filterHandler->getFilterSettings();
$query = $this
->uploadRepository
->getUploadListQuery()
->getUploadListQuery($filterDto)
;
$pagination = $this->paginator->paginate(
@@ -35,6 +39,7 @@ class IndexController extends AbstractController
return $this->render('administrative/document/index.html.twig', [
'pagination' => $pagination,
'filterDto' => $filterDto,
]);
}
}
@@ -0,0 +1,54 @@
<?php
namespace App\Controller\Common;
use App\Controller\Traits\ReturnUrlTrait;
use App\Form\DocumentFilterType;
use App\Htmx\HxRedirectResponse;
use App\Service\Common\DocumentFilterHandler;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\Security\Http\Attribute\IsGranted;
class DocumentFilterController extends AbstractController
{
use ReturnUrlTrait;
public function __construct(
private readonly DocumentFilterHandler $filterHandler
) {
}
#[Route('/common/document/filter', name: 'app_common_document_filter')]
#[IsGranted('ROLE_ADMINISTRATIVE')]
public function index(Request $request): Response
{
$formData = $this->filterHandler->getFilterSettings();
$form = $this->createForm(DocumentFilterType::class, $formData);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$this->filterHandler->handleRequest($form);
$returnUrl = $this->getReturnUrl($request, 'app_administrative_document_index');
return new HxRedirectResponse($returnUrl);
}
return $this->render('common/modal_document_filter.html.twig', [
'filterForm' => $form->createView(),
'filterDto' => $form->getData(),
]);
}
#[Route('/common/document/filter/reset', name: 'app_common_document_filter_reset')]
public function reset(Request $request): Response
{
$this->filterHandler->resetFilterSettings();
$returnUrl = $this->getReturnUrl($request, 'app_administrative_document_index');
return $this->redirect($returnUrl);
}
}
+64
View File
@@ -0,0 +1,64 @@
<?php
namespace App\Form;
use App\Entity\Teamer;
use App\Entity\Upload;
use App\Model\DocumentFilterDto;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class DocumentFilterType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$builder
->add('teamer', AutocompleteEntityType::class, [
'label' => 'Teamer',
'class' => Teamer::class,
'label_property' => 'name',
'label_function' => fn(Teamer $teamer) => (string) $teamer,
'endpoint_route' => 'app_admin_autocomplete_teamer',
])
->add('type', ChoiceType::class, [
'label' => 'Typ',
'required' => false,
'placeholder' => 'nicht filtern',
'choices' => [
'Honorarnote' => Upload::TYPE_INVOICE,
'Honorarvertrag' => Upload::TYPE_CONTRACT,
],
])
->add('dateFrom', DatepickerType::class, [
'label' => 'Einsatzzeitraum von',
'attr' => [
'placeholder' => 'nicht filtern',
],
])
->add('dateTo', DatepickerType::class, [
'label' => 'Einsatzzeitraum bis',
'attr' => [
'placeholder' => 'nicht filtern',
],
])
->add('apply', SubmitType::class, [
'label' => 'filtern',
])
->add('reset', SubmitType::class, [
'label' => 'reset',
])
;
}
public function configureOptions(OptionsResolver $resolver): void
{
$resolver
->setDefaults([
'data_class' => DocumentFilterDto::class,
])
;
}
}
+61
View File
@@ -0,0 +1,61 @@
<?php
namespace App\Model;
use App\Entity\Teamer;
class DocumentFilterDto extends AbstractFilterDto
{
protected ?Teamer $teamer = null;
protected ?string $type = null;
protected ?\DateTimeImmutable $dateFrom = null;
protected ?\DateTimeImmutable $dateTo = null;
public function getTeamer(): ?Teamer
{
return $this->teamer;
}
public function setTeamer(?Teamer $teamer): static
{
$this->teamer = $teamer;
return $this;
}
public function getType(): ?string
{
return $this->type;
}
public function setType(?string $type): static
{
$this->type = $type;
return $this;
}
public function getDateFrom(): ?\DateTimeImmutable
{
return $this->dateFrom;
}
public function setDateFrom(?\DateTimeImmutable $dateFrom): static
{
$this->dateFrom = $dateFrom;
return $this;
}
public function getDateTo(): ?\DateTimeImmutable
{
return $this->dateTo;
}
public function setDateTo(?\DateTimeImmutable $dateTo): static
{
$this->dateTo = $dateTo;
return $this;
}
}
+33 -3
View File
@@ -3,6 +3,7 @@
namespace App\Repository;
use App\Entity\Upload;
use App\Model\DocumentFilterDto;
use App\Repository\Traits\QueryHelperTrait;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\ORM\Query;
@@ -78,11 +79,11 @@ class UploadRepository extends ServiceEntityRepository
return $data;
}
public function getUploadListQuery(): Query
public function getUploadListQuery(DocumentFilterDto $filterDto): Query
{
$qb = $this->createQueryBuilder('upload');
return $qb
$qb
->select('upload', 'disposition', 'assignment', 'job_profile', 'destination', 'teamer')
->innerJoin('upload.disposition', 'disposition')
->innerJoin('disposition.assignment', 'assignment')
@@ -92,8 +93,37 @@ class UploadRepository extends ServiceEntityRepository
->addOrderBy('FIELD(upload.status, :new, :checked)', 'ASC')
->setParameter('new', Upload::STATUS_NEW)
->setParameter('checked', Upload::STATUS_CHECKED)
->getQuery()
;
if (null !== $teamer = $filterDto->getTeamer()) {
$qb
->andWhere($qb->expr()->eq('upload.teamer', ':teamer'))
->setParameter('teamer', $teamer)
;
}
if (null !== $type = $filterDto->getType()) {
$qb
->andWhere($qb->expr()->eq('upload.type', ':type'))
->setParameter('type', $type)
;
}
if (null !== $dateFrom = $filterDto->getDateFrom()) {
$qb
->andWhere($qb->expr()->gte('destination.dateFrom', ':dateFrom'))
->setParameter('dateFrom', $dateFrom)
;
}
if (null !== $dateTo = $filterDto->getDateTo()) {
$qb
->andWhere($qb->expr()->lte('destination.dateTo', ':dateTo'))
->setParameter('dateTo', $dateTo)
;
}
return $qb->getQuery();
}
public function getNew(int $limit = 5): array
@@ -0,0 +1,60 @@
<?php
namespace App\Service\Common;
use App\Model\AbstractFilterDto;
use App\Model\AssignmentFilterDto;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\HttpFoundation\Session\SessionInterface;
abstract class AbstractFilterHandler
{
protected string $namespace = 'filter:default';
public function __construct(
protected readonly RequestStack $requestStack,
protected readonly EntityManagerInterface $entityManager
) {
}
public function setNamespace(string $namespace): static
{
$this->namespace = $namespace;
return $this;
}
public function handleRequest(FormInterface $form): AbstractFilterDto
{
$filterDto = $form->getData();
if ($form->get('reset')->isClicked()) {
$filterDto = new AssignmentFilterDto();
$this->resetFilterSettings();
} elseif ($form->get('apply')->isClicked()) {
$this->saveFilterSettings($filterDto);
}
return $filterDto;
}
public function resetFilterSettings(): void
{
$this->getSession()->remove($this->namespace);
}
public function getFilterSettings(): AbstractFilterDto
{
}
protected function saveFilterSettings(AbstractFilterDto $filterDto): void
{
}
protected function getSession(): SessionInterface
{
return $this->requestStack->getSession();
}
}
+5 -44
View File
@@ -3,47 +3,12 @@
namespace App\Service\Common;
use App\Entity\JobProfile;
use App\Model\AbstractFilterDto;
use App\Model\AssignmentFilterDto;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\HttpFoundation\Session\SessionInterface;
class AssignmentFilterHandler
class AssignmentFilterHandler extends AbstractFilterHandler
{
private string $namespace = 'filter:assignment';
public function __construct(
private readonly RequestStack $requestStack,
private readonly EntityManagerInterface $entityManager
) {
}
public function setNamespace(string $namespace): static
{
$this->namespace = $namespace;
return $this;
}
public function handleRequest(FormInterface $form): AssignmentFilterDto
{
$filterDto = $form->getData();
if ($form->get('reset')->isClicked()) {
$filterDto = new AssignmentFilterDto();
$this->resetFilterSettings();
} elseif ($form->get('apply')->isClicked()) {
$this->saveFilterSettings($filterDto);
}
return $filterDto;
}
public function resetFilterSettings(): void
{
$this->getSession()->remove($this->namespace);
}
protected string $namespace = 'filter:assignment';
public function getFilterSettings(): AssignmentFilterDto
{
@@ -83,8 +48,9 @@ class AssignmentFilterHandler
return $filterDto;
}
private function saveFilterSettings(AssignmentFilterDto $filterDto): void
protected function saveFilterSettings(AbstractFilterDto $filterDto): void
{
/** @var AssignmentFilterDto $filterDto */
$this->getSession()->set($this->namespace, [
'date_from' => $filterDto->getDateFrom(),
'date_to' => $filterDto->getDateTo(),
@@ -97,9 +63,4 @@ class AssignmentFilterHandler
'include_past' => $filterDto->isIncludePast(),
]);
}
private function getSession(): SessionInterface
{
return $this->requestStack->getSession();
}
}
@@ -0,0 +1,49 @@
<?php
namespace App\Service\Common;
use App\Entity\Teamer;
use App\Model\AbstractFilterDto;
use App\Model\DocumentFilterDto;
class DocumentFilterHandler extends AbstractFilterHandler
{
protected string $namespace = 'filter:document';
public function getFilterSettings(): DocumentFilterDto
{
if (null === $data = $this->getSession()->get($this->namespace)) {
return new DocumentFilterDto();
}
$filterDto = new DocumentFilterDto();
$filterDto
->setType($data['type'])
->setDateFrom($data['date_from'])
->setDateTo($data['date_to'])
;
if (isset($data['teamer_id'])) {
$teamer = $this
->entityManager
->getRepository(Teamer::class)
->find($data['teamer_id'])
;
$filterDto->setTeamer($teamer);
}
return $filterDto;
}
protected function saveFilterSettings(AbstractFilterDto $filterDto): void
{
/** @var DocumentFilterDto $filterDto */
$this->getSession()->set($this->namespace, [
'teamer_id' => $filterDto->getTeamer()?->getId(),
'type' => $filterDto->getType(),
'date_from' => $filterDto->getDateFrom(),
'date_to' => $filterDto->getDateTo(),
]);
}
}
+5 -44
View File
@@ -3,47 +3,12 @@
namespace App\Service\Common;
use App\Entity\JobProfile;
use App\Model\AbstractFilterDto;
use App\Model\TeamerFilterDto;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\HttpFoundation\Session\SessionInterface;
class TeamerFilterHandler
class TeamerFilterHandler extends AbstractFilterHandler
{
private string $namespace = 'filter:teamer';
public function __construct(
private readonly RequestStack $requestStack,
private readonly EntityManagerInterface $entityManager
) {
}
public function setNamespace(string $namespace): static
{
$this->namespace = $namespace;
return $this;
}
public function handleRequest(FormInterface $form): TeamerFilterDto
{
$filterDto = $form->getData();
if ($form->get('reset')->isClicked()) {
$filterDto = new TeamerFilterDto();
$this->resetFilterSettings();
} elseif ($form->get('apply')->isClicked()) {
$this->saveFilterSettings($filterDto);
}
return $filterDto;
}
public function resetFilterSettings(): void
{
$this->getSession()->remove($this->namespace);
}
protected string $namespace = 'filter:teamer';
public function getFilterSettings(): TeamerFilterDto
{
@@ -74,8 +39,9 @@ class TeamerFilterHandler
return $filterDto;
}
private function saveFilterSettings(TeamerFilterDto $filterDto): void
protected function saveFilterSettings(AbstractFilterDto $filterDto): void
{
/** @var TeamerFilterDto $filterDto */
$this->getSession()->set($this->namespace, [
'name' => $filterDto->getName(),
'available_at' => $filterDto->getAvailableAt(),
@@ -88,9 +54,4 @@ class TeamerFilterHandler
}, $filterDto->getJobProfiles()),
]);
}
private function getSession(): SessionInterface
{
return $this->requestStack->getSession();
}
}