diff --git a/src/Controller/PartListsController.php b/src/Controller/PartListsController.php index 91a149ea..db000c8d 100644 --- a/src/Controller/PartListsController.php +++ b/src/Controller/PartListsController.php @@ -43,6 +43,7 @@ declare(strict_types=1); namespace App\Controller; use App\DataTables\Filters\PartFilter; +use App\DataTables\Filters\PartSearchFilter; use App\DataTables\PartsDataTable; use App\Entity\Parts\Category; use App\Entity\Parts\Footprint; @@ -282,6 +283,26 @@ class PartListsController extends AbstractController ); } + private function searchRequestToFilter(Request $request): PartSearchFilter + { + $filter = new PartSearchFilter($request->query->get('keyword', '')); + + $filter->setName($request->query->getBoolean('name', true)); + $filter->setCategory($request->query->getBoolean('category', true)); + $filter->setDescription($request->query->getBoolean('description', true)); + $filter->setTags($request->query->getBoolean('tags', true)); + $filter->setStorelocation($request->query->getBoolean('storelocation', true)); + $filter->setComment($request->query->getBoolean('comment', true)); + $filter->setOrdernr($request->query->getBoolean('ordernr', true)); + $filter->setSupplier($request->query->getBoolean('supplier', false)); + $filter->setManufacturer($request->query->getBoolean('manufacturer', false)); + $filter->setFootprint($request->query->getBoolean('footprint', false)); + + $filter->setRegex($request->query->getBoolean('regex', false)); + + return $filter; + } + /** * @Route("/parts/search", name="parts_search") * @@ -289,25 +310,10 @@ class PartListsController extends AbstractController */ public function showSearch(Request $request, DataTableFactory $dataTable) { - $search = $request->query->get('keyword', ''); - $search_options = [ - 'name' => $request->query->getBoolean('name'), - 'description' => $request->query->getBoolean('description'), - 'comment' => $request->query->getBoolean('comment'), - 'category' => $request->query->getBoolean('category'), - 'store_location' => $request->query->getBoolean('storelocation'), - 'supplier' => $request->query->getBoolean('supplier'), - 'ordernr' => $request->query->getBoolean('ordernr'), - 'manufacturer' => $request->query->getBoolean('manufacturer'), - 'footprint' => $request->query->getBoolean('footprint'), - 'tags' => $request->query->getBoolean('tags'), - 'regex' => $request->query->getBoolean('regex'), - ]; - + $searchFilter = $this->searchRequestToFilter($request); $table = $dataTable->createFromType(PartsDataTable::class, [ - 'search' => $search, - 'search_options' => $search_options, + 'search' => $searchFilter, ])->handleRequest($request); if ($table->isCallback()) { @@ -316,7 +322,7 @@ class PartListsController extends AbstractController return $this->render('Parts/lists/search_list.html.twig', [ 'datatable' => $table, - 'keyword' => $search, + 'keyword' => $searchFilter->getQuery(), ]); } diff --git a/src/DataTables/Filters/PartSearchFilter.php b/src/DataTables/Filters/PartSearchFilter.php new file mode 100644 index 00000000..98364c75 --- /dev/null +++ b/src/DataTables/Filters/PartSearchFilter.php @@ -0,0 +1,359 @@ +query = $query; + } + + protected function getFieldsToSearch(): array + { + $fields_to_search = []; + + if($this->name) { + $fields_to_search[] = 'part.name'; + } + if($this->category) { + $fields_to_search[] = 'category.name'; + } + if($this->description) { + $fields_to_search[] = 'part.description'; + } + if($this->tags) { + $fields_to_search[] = 'part.tags'; + } + if($this->storelocation) { + $fields_to_search[] = 'storelocations.name'; + } + if($this->ordernr) { + $fields_to_search[] = 'orderdetails.supplierpartnr'; + } + if($this->mpn) { + $fields_to_search[] = 'part.manufacturer_product_url'; + } + if($this->supplier) { + $fields_to_search[] = 'suppliers.name'; + } + if($this->manufacturer) { + $fields_to_search[] = 'manufacturer.name'; + } + if($this->footprint) { + $fields_to_search[] = 'footprint.name'; + } + + return $fields_to_search; + } + + public function apply(QueryBuilder $queryBuilder): void + { + $fields_to_search = $this->getFieldsToSearch(); + + //If we have nothing to search for, do nothing + if (empty($fields_to_search) || empty($this->query)) { + return; + } + + //Convert the fields to search to a list of expressions + $expressions = array_map(function (string $field) { + if ($this->regex) { + return sprintf("REGEXP(%s, :search_query) = 1", $field); + } + + return sprintf("%s LIKE :search_query", $field); + }, $fields_to_search); + + //Add Or concatation of the expressions to our query + $queryBuilder->andWhere( + $queryBuilder->expr()->orX(...$expressions) + ); + + //For regex we pass the query as is, for like we add % to the start and end as wildcards + if ($this->regex) { + $queryBuilder->setParameter('search_query', $this->query); + } else { + $queryBuilder->setParameter('search_query', '%' . $this->query . '%'); + } + } + + /** + * @return string + */ + public function getQuery(): string + { + return $this->query; + } + + /** + * @param string $query + * @return PartSearchFilter + */ + public function setQuery(string $query): PartSearchFilter + { + $this->query = $query; + return $this; + } + + /** + * @return bool + */ + public function isRegex(): bool + { + return $this->regex; + } + + /** + * @param bool $regex + * @return PartSearchFilter + */ + public function setRegex(bool $regex): PartSearchFilter + { + $this->regex = $regex; + return $this; + } + + /** + * @return bool + */ + public function isName(): bool + { + return $this->name; + } + + /** + * @param bool $name + * @return PartSearchFilter + */ + public function setName(bool $name): PartSearchFilter + { + $this->name = $name; + return $this; + } + + /** + * @return bool + */ + public function isCategory(): bool + { + return $this->category; + } + + /** + * @param bool $category + * @return PartSearchFilter + */ + public function setCategory(bool $category): PartSearchFilter + { + $this->category = $category; + return $this; + } + + /** + * @return bool + */ + public function isDescription(): bool + { + return $this->description; + } + + /** + * @param bool $description + * @return PartSearchFilter + */ + public function setDescription(bool $description): PartSearchFilter + { + $this->description = $description; + return $this; + } + + /** + * @return bool + */ + public function isTags(): bool + { + return $this->tags; + } + + /** + * @param bool $tags + * @return PartSearchFilter + */ + public function setTags(bool $tags): PartSearchFilter + { + $this->tags = $tags; + return $this; + } + + /** + * @return bool + */ + public function isStorelocation(): bool + { + return $this->storelocation; + } + + /** + * @param bool $storelocation + * @return PartSearchFilter + */ + public function setStorelocation(bool $storelocation): PartSearchFilter + { + $this->storelocation = $storelocation; + return $this; + } + + /** + * @return bool + */ + public function isOrdernr(): bool + { + return $this->ordernr; + } + + /** + * @param bool $ordernr + * @return PartSearchFilter + */ + public function setOrdernr(bool $ordernr): PartSearchFilter + { + $this->ordernr = $ordernr; + return $this; + } + + /** + * @return bool + */ + public function isMpn(): bool + { + return $this->mpn; + } + + /** + * @param bool $mpn + * @return PartSearchFilter + */ + public function setMpn(bool $mpn): PartSearchFilter + { + $this->mpn = $mpn; + return $this; + } + + /** + * @return bool + */ + public function isSupplier(): bool + { + return $this->supplier; + } + + /** + * @param bool $supplier + * @return PartSearchFilter + */ + public function setSupplier(bool $supplier): PartSearchFilter + { + $this->supplier = $supplier; + return $this; + } + + /** + * @return bool + */ + public function isManufacturer(): bool + { + return $this->manufacturer; + } + + /** + * @param bool $manufacturer + * @return PartSearchFilter + */ + public function setManufacturer(bool $manufacturer): PartSearchFilter + { + $this->manufacturer = $manufacturer; + return $this; + } + + /** + * @return bool + */ + public function isFootprint(): bool + { + return $this->footprint; + } + + /** + * @param bool $footprint + * @return PartSearchFilter + */ + public function setFootprint(bool $footprint): PartSearchFilter + { + $this->footprint = $footprint; + return $this; + } + + /** + * @return bool + */ + public function isComment(): bool + { + return $this->comment; + } + + /** + * @param bool $comment + * @return PartSearchFilter + */ + public function setComment(bool $comment): PartSearchFilter + { + $this->comment = $comment; + return $this; + } + + +} \ No newline at end of file diff --git a/src/DataTables/PartsDataTable.php b/src/DataTables/PartsDataTable.php index bdfb33af..986e5278 100644 --- a/src/DataTables/PartsDataTable.php +++ b/src/DataTables/PartsDataTable.php @@ -50,6 +50,7 @@ use App\DataTables\Column\PartAttachmentsColumn; use App\DataTables\Column\PrettyBoolColumn; use App\DataTables\Column\TagsColumn; use App\DataTables\Filters\PartFilter; +use App\DataTables\Filters\PartSearchFilter; use App\Entity\Parts\Category; use App\Entity\Parts\Footprint; use App\Entity\Parts\Manufacturer; @@ -104,49 +105,12 @@ final class PartsDataTable implements DataTableTypeInterface public function configureOptions(OptionsResolver $optionsResolver): void { $optionsResolver->setDefaults([ - 'category' => null, - 'footprint' => null, - 'manufacturer' => null, - 'storelocation' => null, - 'supplier' => null, - 'tag' => null, - 'search' => null, - 'filter' => null + 'filter' => null, + 'search' => null ]); - $optionsResolver->setAllowedTypes('category', ['null', Category::class]); - $optionsResolver->setAllowedTypes('footprint', ['null', Footprint::class]); - $optionsResolver->setAllowedTypes('manufacturer', ['null', Manufacturer::class]); - $optionsResolver->setAllowedTypes('supplier', ['null', Supplier::class]); - $optionsResolver->setAllowedTypes('tag', ['null', 'string']); - $optionsResolver->setAllowedTypes('search', ['null', 'string']); - - //Configure search options - $optionsResolver->setDefault('search_options', static function (OptionsResolver $resolver): void { - $resolver->setDefaults([ - 'name' => true, - 'category' => true, - 'description' => true, - 'store_location' => true, - 'comment' => true, - 'ordernr' => true, - 'supplier' => false, - 'manufacturer' => false, - 'footprint' => false, - 'tags' => false, - 'regex' => false, - ]); - $resolver->setAllowedTypes('name', 'bool'); - $resolver->setAllowedTypes('category', 'bool'); - $resolver->setAllowedTypes('description', 'bool'); - $resolver->setAllowedTypes('store_location', 'bool'); - $resolver->setAllowedTypes('comment', 'bool'); - $resolver->setAllowedTypes('supplier', 'bool'); - $resolver->setAllowedTypes('manufacturer', 'bool'); - $resolver->setAllowedTypes('footprint', 'bool'); - $resolver->setAllowedTypes('tags', 'bool'); - $resolver->setAllowedTypes('regex', 'bool'); - }); + $optionsResolver->setAllowedTypes('filter', [PartFilter::class, 'null']); + $optionsResolver->setAllowedTypes('search', [PartSearchFilter::class, 'null']); } public function configure(DataTable $dataTable, array $options): void @@ -365,101 +329,17 @@ final class PartsDataTable implements DataTableTypeInterface private function buildCriteria(QueryBuilder $builder, array $options): void { + //Apply the search criterias first + if ($options['search'] instanceof PartSearchFilter) { + $search = $options['search']; + $search->apply($builder); + } + //We do the most stuff here in the filter class - if (isset($options['filter']) && $options['filter'] instanceof PartFilter) { + if ($options['filter'] instanceof PartFilter) { $filter = $options['filter']; $filter->apply($builder); } - if (!empty($options['search'])) { - if (!$options['search_options']['regex']) { - //Dont show results, if no things are selected - $builder->andWhere('0=1'); - $defined = false; - if ($options['search_options']['name']) { - $builder->orWhere('part.name LIKE :search'); - $defined = true; - } - if ($options['search_options']['description']) { - $builder->orWhere('part.description LIKE :search'); - $defined = true; - } - if ($options['search_options']['comment']) { - $builder->orWhere('part.comment LIKE :search'); - $defined = true; - } - if ($options['search_options']['category']) { - $builder->orWhere('category.name LIKE :search'); - $defined = true; - } - if ($options['search_options']['manufacturer']) { - $builder->orWhere('manufacturer.name LIKE :search'); - $defined = true; - } - if ($options['search_options']['footprint']) { - $builder->orWhere('footprint.name LIKE :search'); - $defined = true; - } - if ($options['search_options']['tags']) { - $builder->orWhere('part.tags LIKE :search'); - $defined = true; - } - if ($options['search_options']['store_location']) { - $builder->orWhere('storelocations.name LIKE :search'); - $defined = true; - } - if ($options['search_options']['supplier']) { - $builder->orWhere('suppliers.name LIKE :search'); - $defined = true; - } - - if ($defined) { - $builder->setParameter('search', '%'.$options['search'].'%'); - } - } else { //Use REGEX - $builder->andWhere('0=1'); - $defined = false; - if ($options['search_options']['name']) { - $builder->orWhere('REGEXP(part.name, :search) = 1'); - $defined = true; - } - if ($options['search_options']['description']) { - $builder->orWhere('REGEXP(part.description, :search) = 1'); - $defined = true; - } - if ($options['search_options']['comment']) { - $builder->orWhere('REGEXP(part.comment, :search) = 1'); - $defined = true; - } - if ($options['search_options']['category']) { - $builder->orWhere('REGEXP(category.name, :search) = 1'); - $defined = true; - } - if ($options['search_options']['manufacturer']) { - $builder->orWhere('REGEXP(manufacturer.name, :search) = 1'); - $defined = true; - } - if ($options['search_options']['footprint']) { - $builder->orWhere('REGEXP(footprint.name, :search) = 1'); - $defined = true; - } - if ($options['search_options']['tags']) { - $builder->orWhere('REGEXP(part.tags, :search) = 1'); - $defined = true; - } - if ($options['search_options']['store_location']) { - $builder->orWhere('REGEXP(storelocations.name, :search) = 1'); - $defined = true; - } - if ($options['search_options']['supplier']) { - $builder->orWhere('REGEXP(suppliers.name, :search) = 1'); - $defined = true; - } - - if ($defined) { - $builder->setParameter('search', $options['search']); - } - } - } } } diff --git a/templates/_navbar_search.html.twig b/templates/_navbar_search.html.twig index 14c092e6..514a203a 100644 --- a/templates/_navbar_search.html.twig +++ b/templates/_navbar_search.html.twig @@ -18,6 +18,10 @@ +
+ + +
@@ -26,8 +30,8 @@
-
- +
+
{% if true %} @@ -52,10 +56,7 @@
{% endif %} -
- - -
+