diff --git a/src/DataTables/Filters/PartSearchFilter.php b/src/DataTables/Filters/PartSearchFilter.php index 6e2e5894..1be5fed1 100644 --- a/src/DataTables/Filters/PartSearchFilter.php +++ b/src/DataTables/Filters/PartSearchFilter.php @@ -65,11 +65,16 @@ class PartSearchFilter implements FilterInterface /** @var bool Use Internal Part number for searching */ protected bool $ipn = true; + /** @var int Helper variable for hacky array_map variable injection */ + protected int $it = 0; + public function __construct( /** @var string The string to query for */ protected string $keyword ) { + // Transform keyword and trim excess spaces + $keyword = trim(str_replace('+', ' ', $keyword)); } protected function getFieldsToSearch(): array @@ -125,26 +130,50 @@ public function apply(QueryBuilder $queryBuilder): void return; } - //Convert the fields to search to a list of expressions - $expressions = array_map(function (string $field): string { - if ($this->regex) { + if($this->regex) { + //Convert the fields to search to a list of expressions + $expressions = array_map(function (string $field): string { return sprintf("REGEXP(%s, :search_query) = TRUE", $field); - } - - return sprintf("ILIKE(%s, :search_query) = TRUE", $field); - }, $fields_to_search); + }, $fields_to_search); - //Add Or concatenation of the expressions to our query - $queryBuilder->andWhere( - $queryBuilder->expr()->orX(...$expressions) - ); + //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) { + //For regex, we pass the query as is, save html special chars $queryBuilder->setParameter('search_query', $this->keyword); - } else { - $queryBuilder->setParameter('search_query', '%' . $this->keyword . '%'); + return; } + + //Split keyword on spaces, but limit token count to not blow up the DB + $tokens = explode(' ', $this->keyword, 5); + + $params = new \Doctrine\Common\Collections\ArrayCollection(); + + //Perform search of every single token in every selected field, AND the where clauses + for ($i = 0; $i < sizeof($tokens); $i++) { + $this->it = $i; + $tokens[$i] = trim($tokens[$i]); + + //Skip empty words (e.g. because of multiple spaces) + if ($tokens[$i] === '') continue; + + //Convert the fields to search to a list of expressions + $expressions = array_map(function (string $field): string { + return sprintf("ILIKE(%s, :search_query%u) = TRUE", $field, $this->it); + }, $fields_to_search); + + //Aggregate the parameters for consolidated commission + $params[] = new \Doctrine\ORM\Query\Parameter('search_query' . $i, '%' . $tokens[$i] . '%'); + + //Add Or concatenation of the expressions to our query + $queryBuilder->andWhere( + $queryBuilder->expr()->orX(...$expressions) + ); + } + $queryBuilder->setParameters($params); + } public function getKeyword(): string @@ -301,5 +330,4 @@ public function setComment(bool $comment): PartSearchFilter return $this; } - }