diff --git a/src/Controller/PartListsController.php b/src/Controller/PartListsController.php index 808b0c5d..2210fc18 100644 --- a/src/Controller/PartListsController.php +++ b/src/Controller/PartListsController.php @@ -319,6 +319,7 @@ class PartListsController extends AbstractController //As an unchecked checkbox is not set in the query, the default value for all bools have to be false (which is the default argument value)! $filter->setName($request->query->getBoolean('name')); + $filter->setDbId($request->query->getBoolean('dbid')); $filter->setCategory($request->query->getBoolean('category')); $filter->setDescription($request->query->getBoolean('description')); $filter->setMpn($request->query->getBoolean('mpn')); diff --git a/src/DataTables/Filters/PartSearchFilter.php b/src/DataTables/Filters/PartSearchFilter.php index aa8c20f4..c0951d3a 100644 --- a/src/DataTables/Filters/PartSearchFilter.php +++ b/src/DataTables/Filters/PartSearchFilter.php @@ -23,6 +23,7 @@ declare(strict_types=1); namespace App\DataTables\Filters; use App\DataTables\Filters\Constraints\AbstractConstraint; use Doctrine\ORM\QueryBuilder; +use Doctrine\DBAL\ParameterType; class PartSearchFilter implements FilterInterface { @@ -33,6 +34,9 @@ class PartSearchFilter implements FilterInterface /** @var bool Use name field for searching */ protected bool $name = true; + /** @var bool Use id field for searching */ + protected bool $dbId = false; + /** @var bool Use category name for searching */ protected bool $category = true; @@ -120,33 +124,51 @@ class PartSearchFilter implements FilterInterface public function apply(QueryBuilder $queryBuilder): void { $fields_to_search = $this->getFieldsToSearch(); + $is_numeric = preg_match('/^\d+$/', $this->keyword) === 1; + + // Add exact ID match only when the keyword is numeric + $search_dbId = $is_numeric && (bool)$this->dbId; //If we have nothing to search for, do nothing - if ($fields_to_search === [] || $this->keyword === '') { + if (($fields_to_search === [] && !$search_dbId) || $this->keyword === '') { return; } - //Convert the fields to search to a list of expressions - $expressions = array_map(function (string $field): string { + $expressions = []; + + if($fields_to_search !== []) { + //Convert the fields to search to a list of expressions + $expressions = array_map(function (string $field): string { + if ($this->regex) { + return sprintf("REGEXP(%s, :search_query) = TRUE", $field); + } + + return sprintf("ILIKE(%s, :search_query) = TRUE", $field); + }, $fields_to_search); + + //For regex, we pass the query as is, for like we add % to the start and end as wildcards if ($this->regex) { - return sprintf("REGEXP(%s, :search_query) = TRUE", $field); + $queryBuilder->setParameter('search_query', $this->keyword); + } else { + //Escape % and _ characters in the keyword + $this->keyword = str_replace(['%', '_'], ['\%', '\_'], $this->keyword); + $queryBuilder->setParameter('search_query', '%' . $this->keyword . '%'); } + } - return sprintf("ILIKE(%s, :search_query) = TRUE", $field); - }, $fields_to_search); + //Use equal expression to just search for exact numeric matches + if ($search_dbId) { + $expressions[] = $queryBuilder->expr()->eq('part.id', ':id_exact'); + $queryBuilder->setParameter('id_exact', (int) $this->keyword, + \Doctrine\DBAL\ParameterType::INTEGER); + } - //Add Or concatenation 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->keyword); - } else { - //Escape % and _ characters in the keyword - $this->keyword = str_replace(['%', '_'], ['\%', '\_'], $this->keyword); - $queryBuilder->setParameter('search_query', '%' . $this->keyword . '%'); + //Guard condition + if (!empty($expressions)) { + //Add Or concatenation of the expressions to our query + $queryBuilder->andWhere( + $queryBuilder->expr()->orX(...$expressions) + ); } } @@ -183,6 +205,17 @@ class PartSearchFilter implements FilterInterface return $this; } + public function isDbId(): bool + { + return $this->dbId; + } + + public function setDbId(bool $dbId): PartSearchFilter + { + $this->dbId = $dbId; + return $this; + } + public function isCategory(): bool { return $this->category; diff --git a/templates/components/search.macro.html.twig b/templates/components/search.macro.html.twig index e62af2b1..a324ad35 100644 --- a/templates/components/search.macro.html.twig +++ b/templates/components/search.macro.html.twig @@ -11,6 +11,10 @@ +