From 017b0f717eb492306c4ff2dcfca7fe89413b1095 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20B=C3=B6hmer?= Date: Sun, 11 Sep 2022 02:00:22 +0200 Subject: [PATCH] Added filter possibility to attachment list --- src/Controller/AttachmentFileController.php | 19 ++- src/DataTables/AttachmentDataTable.php | 22 ++++ src/DataTables/Filters/AttachmentFilter.php | 120 ++++++++++++++++++ .../Constraints/InstanceOfConstraint.php | 96 ++++++++++++++ src/Form/Filters/AttachmentFilterType.php | 101 +++++++++++++++ .../Constraints/InstanceOfConstraintType.php | 30 +++++ templates/attachment_list.html.twig | 48 +++++++ translations/messages.en.xlf | 6 + 8 files changed, 440 insertions(+), 2 deletions(-) create mode 100644 src/DataTables/Filters/AttachmentFilter.php create mode 100644 src/DataTables/Filters/Constraints/InstanceOfConstraint.php create mode 100644 src/Form/Filters/AttachmentFilterType.php create mode 100644 src/Form/Filters/Constraints/InstanceOfConstraintType.php diff --git a/src/Controller/AttachmentFileController.php b/src/Controller/AttachmentFileController.php index 9d9ba2f1..34501024 100644 --- a/src/Controller/AttachmentFileController.php +++ b/src/Controller/AttachmentFileController.php @@ -43,9 +43,15 @@ declare(strict_types=1); namespace App\Controller; use App\DataTables\AttachmentDataTable; +use App\DataTables\Filters\AttachmentFilter; +use App\DataTables\Filters\PartFilter; +use App\DataTables\PartsDataTable; use App\Entity\Attachments\Attachment; use App\Entity\Attachments\PartAttachment; +use App\Form\Filters\AttachmentFilterType; +use App\Form\Filters\PartFilterType; use App\Services\Attachments\AttachmentManager; +use App\Services\Trees\NodesListBuilder; use Omines\DataTablesBundle\DataTableFactory; use RuntimeException; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; @@ -123,11 +129,19 @@ class AttachmentFileController extends AbstractController * * @return JsonResponse|Response */ - public function attachmentsTable(DataTableFactory $dataTable, Request $request) + public function attachmentsTable(Request $request, DataTableFactory $dataTableFactory, NodesListBuilder $nodesListBuilder) { $this->denyAccessUnlessGranted('read', new PartAttachment()); - $table = $dataTable->createFromType(AttachmentDataTable::class) + $formRequest = clone $request; + $formRequest->setMethod('GET'); + $filter = new AttachmentFilter($nodesListBuilder); + + $filterForm = $this->createForm(AttachmentFilterType::class, $filter, ['method' => 'GET']); + + $filterForm->handleRequest($formRequest); + + $table = $dataTableFactory->createFromType(AttachmentDataTable::class, ['filter' => $filter]) ->handleRequest($request); if ($table->isCallback()) { @@ -136,6 +150,7 @@ class AttachmentFileController extends AbstractController return $this->render('attachment_list.html.twig', [ 'datatable' => $table, + 'filterForm' => $filterForm->createView(), ]); } } diff --git a/src/DataTables/AttachmentDataTable.php b/src/DataTables/AttachmentDataTable.php index df373ee6..122d3abe 100644 --- a/src/DataTables/AttachmentDataTable.php +++ b/src/DataTables/AttachmentDataTable.php @@ -44,12 +44,14 @@ namespace App\DataTables; use App\DataTables\Column\LocaleDateTimeColumn; use App\DataTables\Column\PrettyBoolColumn; +use App\DataTables\Filters\AttachmentFilter; use App\Entity\Attachments\Attachment; use App\Services\Attachments\AttachmentManager; use App\Services\Attachments\AttachmentURLGenerator; use App\Services\ElementTypeNameGenerator; use App\Services\EntityURLGenerator; use Doctrine\ORM\QueryBuilder; +use Omines\DataTablesBundle\Adapter\Doctrine\ORM\SearchCriteriaProvider; use Omines\DataTablesBundle\Adapter\Doctrine\ORMAdapter; use Omines\DataTablesBundle\Column\TextColumn; use Omines\DataTablesBundle\DataTable; @@ -213,6 +215,12 @@ final class AttachmentDataTable implements DataTableTypeInterface 'query' => function (QueryBuilder $builder): void { $this->getQuery($builder); }, + 'criteria' => [ + function (QueryBuilder $builder) use ($options): void { + $this->buildCriteria($builder, $options); + }, + new SearchCriteriaProvider(), + ], ]); } @@ -225,4 +233,18 @@ final class AttachmentDataTable implements DataTableTypeInterface ->leftJoin('attachment.attachment_type', 'attachment_type'); //->leftJoin('attachment.element', 'element'); } + + private function buildCriteria(QueryBuilder $builder, array $options): void + { + //We do the most stuff here in the filter class + if (isset($options['filter'])) { + if(!$options['filter'] instanceof AttachmentFilter) { + throw new \Exception('filter must be an instance of AttachmentFilter!'); + } + + $filter = $options['filter']; + $filter->apply($builder); + } + + } } diff --git a/src/DataTables/Filters/AttachmentFilter.php b/src/DataTables/Filters/AttachmentFilter.php new file mode 100644 index 00000000..81c33deb --- /dev/null +++ b/src/DataTables/Filters/AttachmentFilter.php @@ -0,0 +1,120 @@ +dbId = new NumberConstraint('attachment.id'); + $this->name = new TextConstraint('attachment.name'); + $this->targetType = new InstanceOfConstraint('attachment'); + $this->attachmentType = new EntityConstraint($nodesListBuilder, AttachmentType::class, 'attachment.attachment_type'); + $this->lastModified = new DateTimeConstraint('attachment.lastModified'); + $this->addedDate = new DateTimeConstraint('attachment.addedDate'); + $this->showInTable = new BooleanConstraint('attachment.show_in_table'); + } + + public function apply(QueryBuilder $queryBuilder): void + { + $this->applyAllChildFilters($queryBuilder); + } + + /** + * @return NumberConstraint + */ + public function getDbId(): NumberConstraint + { + return $this->dbId; + } + + /** + * @return TextConstraint + */ + public function getName(): TextConstraint + { + return $this->name; + } + + /** + * @return DateTimeConstraint + */ + public function getLastModified(): DateTimeConstraint + { + return $this->lastModified; + } + + /** + * @return DateTimeConstraint + */ + public function getAddedDate(): DateTimeConstraint + { + return $this->addedDate; + } + + + /** + * @return BooleanConstraint + */ + public function getShowInTable(): BooleanConstraint + { + return $this->showInTable; + } + + + /** + * @return EntityConstraint + */ + public function getAttachmentType(): EntityConstraint + { + return $this->attachmentType; + } + + /** + * @return InstanceOfConstraint + */ + public function getTargetType(): InstanceOfConstraint + { + return $this->targetType; + } + + + + + + +} \ No newline at end of file diff --git a/src/DataTables/Filters/Constraints/InstanceOfConstraint.php b/src/DataTables/Filters/Constraints/InstanceOfConstraint.php new file mode 100644 index 00000000..c3f11e4a --- /dev/null +++ b/src/DataTables/Filters/Constraints/InstanceOfConstraint.php @@ -0,0 +1,96 @@ +value; + } + + /** + * @param string[] $value + * @return $this + */ + public function setValue(array $value): self + { + $this->value = $value; + return $this; + } + + /** + * @return string + */ + public function getOperator(): string + { + return $this->operator; + } + + /** + * @param string $operator + * @return $this + */ + public function setOperator(string $operator): self + { + $this->operator = $operator; + return $this; + } + + + + public function isEnabled(): bool + { + return !empty($this->operator); + } + + public function apply(QueryBuilder $queryBuilder): void + { + //If no value is provided then we do not apply a filter + if (!$this->isEnabled()) { + return; + } + + //Ensure we have an valid operator + if(!in_array($this->operator, self::ALLOWED_OPERATOR_VALUES, true)) { + throw new \RuntimeException('Invalid operator '. $this->operator . ' provided. Valid operators are '. implode(', ', self::ALLOWED_OPERATOR_VALUES)); + } + + $expressions = []; + + if ($this->operator === 'ANY' || $this->operator === 'NONE') { + foreach($this->value as $value) { + //We cannnot use an paramater here, as this is the only way to pass the FCQN to the query (via binded params, we would need to use ClassMetaData). See: https://github.com/doctrine/orm/issues/4462 + $expressions[] = ($queryBuilder->expr()->isInstanceOf($this->property, $value)); + } + + if($this->operator === 'ANY') { + $queryBuilder->andWhere($queryBuilder->expr()->orX(...$expressions)); + } else { //NONE + $queryBuilder->andWhere($queryBuilder->expr()->not($queryBuilder->expr()->orX(...$expressions))); + } + } else { + throw new \RuntimeException('Unknown operator '. $this->operator . ' provided. Valid operators are '. implode(', ', self::ALLOWED_OPERATOR_VALUES)); + } + } +} \ No newline at end of file diff --git a/src/Form/Filters/AttachmentFilterType.php b/src/Form/Filters/AttachmentFilterType.php new file mode 100644 index 00000000..dfdb9527 --- /dev/null +++ b/src/Form/Filters/AttachmentFilterType.php @@ -0,0 +1,101 @@ +setDefaults([ + 'compound' => true, + 'data_class' => AttachmentFilter::class, + 'csrf_protection' => false, + ]); + } + + public function buildForm(FormBuilderInterface $builder, array $options) + { + $builder->add('dbId', NumberConstraintType::class, [ + 'label' => 'part.filter.dbId', + 'min' => 1, + 'step' => 1, + ]); + + $builder->add('name', TextConstraintType::class, [ + 'label' => 'attachment.edit.name', + ]); + + $builder->add('targetType', InstanceOfConstraintType::class, [ + 'label' => 'attachment.table.element_type', + 'choices' => [ + 'part.label' => PartAttachment::class, + 'attachment_type.label' => AttachmentTypeAttachment::class, + 'category.label' => CategoryAttachment::class, + 'currency.label' => CurrencyAttachment::class, + 'device.label' => DeviceAttachment::class, + 'footprint.label' => FootprintAttachment::class, + 'group.label' => GroupAttachment::class, + 'label_profile.label' => LabelAttachment::class, + 'manufacturer.label' => Manufacturer::class, + 'measurement_unit.label' => MeasurementUnit::class, + 'storelocation.label' => StorelocationAttachment::class, + 'supplier.label' => SupplierAttachment::class, + 'user.label' => UserAttachment::class, + ] + ]); + + $builder->add('attachmentType', StructuralEntityConstraintType::class, [ + 'label' => 'attachment.attachment_type', + 'entity_class' => AttachmentType::class + ]); + + $builder->add('showInTable', BooleanConstraintType::class, [ + 'label' => 'attachment.edit.show_in_table' + ]); + + $builder->add('lastModified', DateTimeConstraintType::class, [ + 'label' => 'lastModified' + ]); + + $builder->add('addedDate', DateTimeConstraintType::class, [ + 'label' => 'createdAt' + ]); + + $builder->add('submit', SubmitType::class, [ + 'label' => 'filter.submit', + ]); + + $builder->add('discard', ResetType::class, [ + 'label' => 'filter.discard', + ]); + } +} \ No newline at end of file diff --git a/src/Form/Filters/Constraints/InstanceOfConstraintType.php b/src/Form/Filters/Constraints/InstanceOfConstraintType.php new file mode 100644 index 00000000..666ae563 --- /dev/null +++ b/src/Form/Filters/Constraints/InstanceOfConstraintType.php @@ -0,0 +1,30 @@ +em = $entityManager; + } + + public function configureOptions(OptionsResolver $resolver) + { + $resolver->setDefault('data_class', InstanceOfConstraint::class); + } + + public function getParent() + { + return ChoiceConstraintType::class; + } +} \ No newline at end of file diff --git a/templates/attachment_list.html.twig b/templates/attachment_list.html.twig index 6715bb97..abb6f4ad 100644 --- a/templates/attachment_list.html.twig +++ b/templates/attachment_list.html.twig @@ -5,5 +5,53 @@ {% block title %}{% trans %}attachment.list.title{% endtrans %}{% endblock %} {% block content %} +
+
+
+ +
+
+
+ +
+
+ + +
+ +
+
+ +
+
+
+ {{ form_start(filterForm, {"attr": {"data-controller": "helpers--form-cleanup", "data-action": "helpers--form-cleanup#submit"}}) }} + + {{ form_row(filterForm.name) }} + {{ form_row(filterForm.attachmentType) }} + {{ form_row(filterForm.targetType) }} + {{ form_row(filterForm.showInTable) }} + {{ form_row(filterForm.lastModified) }} + {{ form_row(filterForm.addedDate) }} + {{ form_row(filterForm.dbId) }} + + {{ form_row(filterForm.submit) }} + {{ form_row(filterForm.discard) }} + +
+
+ +
+
+ + {{ form_end(filterForm) }} +
+
+
+
+ {{ datatables.datatable(datatable) }} {% endblock %} \ No newline at end of file diff --git a/translations/messages.en.xlf b/translations/messages.en.xlf index b28f8df3..de8ef7d6 100644 --- a/translations/messages.en.xlf +++ b/translations/messages.en.xlf @@ -9693,5 +9693,11 @@ Element 3 Enabled search options + + + attachment.table.element_type + Associated element type + +