diff --git a/.docs/filters.md b/.docs/filters.md index 5cb530f4..387b0401 100644 --- a/.docs/filters.md +++ b/.docs/filters.md @@ -158,6 +158,13 @@ $grid->addFilterText('name', 'Name') ->setSplitWordsSearch(false); ``` +If you need to find rows, that contains "foo" and "bar" (not just one of them), you can use `setConjunctionSearch()`. + +```php +$grid->addFilterText('name', 'Name') + ->setConjunctionSearch(); +``` + ## FilterSelect `FilterSelect` has one more parameter - options: diff --git a/src/DataSource/ArrayDataSource.php b/src/DataSource/ArrayDataSource.php index aecc8c17..5d9c59bc 100644 --- a/src/DataSource/ArrayDataSource.php +++ b/src/DataSource/ArrayDataSource.php @@ -209,11 +209,21 @@ protected function applyFilter($row, Filter $filter) $row_value = strtolower(Strings::toAscii((string) $row[$column])); + $found = []; + foreach ($words as $word) { if (strpos($row_value, strtolower(Strings::toAscii($word))) !== false) { - return $row; + if ($filter instanceof FilterText && !$filter->hasConjunctionSearch()) { + return $row; + } else { + $found[] = true; + } } } + + if (count($found) === count($words)) { + return $row; + } } } diff --git a/src/DataSource/DibiFluentDataSource.php b/src/DataSource/DibiFluentDataSource.php index 81336b20..09d20d81 100755 --- a/src/DataSource/DibiFluentDataSource.php +++ b/src/DataSource/DibiFluentDataSource.php @@ -215,7 +215,7 @@ protected function applyFilterText(FilterText $filter): void } if (sizeof($or) > 1) { - $this->dataSource->where('(%or)', $or); + $this->dataSource->where($filter->hasConjunctionSearch() ? '(%and)' : '(%or)', $or); } else { $this->dataSource->where($or); } diff --git a/src/DataSource/DibiFluentMssqlDataSource.php b/src/DataSource/DibiFluentMssqlDataSource.php index fe6b7614..9f7a3341 100755 --- a/src/DataSource/DibiFluentMssqlDataSource.php +++ b/src/DataSource/DibiFluentMssqlDataSource.php @@ -136,7 +136,7 @@ protected function applyFilterText(FilterText $filter): void } if (sizeof($or) > 1) { - $this->dataSource->where('(%or)', $or); + $this->dataSource->where($filter->hasConjunctionSearch() ? '(%and)' : '(%or)', $or); } else { $this->dataSource->where($or); } diff --git a/src/DataSource/DibiFluentPostgreDataSource.php b/src/DataSource/DibiFluentPostgreDataSource.php index 3c9f0807..e798e223 100755 --- a/src/DataSource/DibiFluentPostgreDataSource.php +++ b/src/DataSource/DibiFluentPostgreDataSource.php @@ -33,7 +33,7 @@ protected function applyFilterText(FilterText $filter): void } if (sizeof($or) > 1) { - $this->dataSource->where('(%or)', $or); + $this->dataSource->where($filter->hasConjunctionSearch() ? '(%and)' : '(%or)', $or); } else { $this->dataSource->where($or); } diff --git a/src/DataSource/DoctrineCollectionDataSource.php b/src/DataSource/DoctrineCollectionDataSource.php index 9f4806c1..38580f5a 100755 --- a/src/DataSource/DoctrineCollectionDataSource.php +++ b/src/DataSource/DoctrineCollectionDataSource.php @@ -226,7 +226,7 @@ protected function applyFilterText(FilterText $filter): void } } - $expr = call_user_func_array([Criteria::expr(), 'orX'], $exprs); + $expr = call_user_func_array([Criteria::expr(), $filter->hasConjunctionSearch() ? 'andX' : 'orX'], $exprs); $this->criteria->andWhere($expr); } diff --git a/src/DataSource/DoctrineDataSource.php b/src/DataSource/DoctrineDataSource.php index 7d01d222..65d3f6d7 100755 --- a/src/DataSource/DoctrineDataSource.php +++ b/src/DataSource/DoctrineDataSource.php @@ -319,7 +319,7 @@ protected function applyFilterText(FilterText $filter): void } } - $or = call_user_func_array([$this->dataSource->expr(), 'orX'], $exprs); + $or = call_user_func_array([$this->dataSource->expr(), $filter->hasConjunctionSearch() ? 'andX' : 'orX'], $exprs); $this->dataSource->andWhere($or); } diff --git a/src/DataSource/NetteDatabaseTableDataSource.php b/src/DataSource/NetteDatabaseTableDataSource.php index 67be9bd6..0de2f73f 100755 --- a/src/DataSource/NetteDatabaseTableDataSource.php +++ b/src/DataSource/NetteDatabaseTableDataSource.php @@ -224,31 +224,33 @@ protected function applyFilterText(FilterText $filter): void $bigOrArgs = []; $condition = $filter->getCondition(); + $operator = $filter->hasConjunctionSearch() ? 'AND' : 'OR'; + foreach ($condition as $column => $value) { $like = '('; $args = []; if ($filter->isExactSearch()) { - $like .= "$column = ? OR "; + $like .= "$column = ? $operator "; $args[] = "$value"; } else { $words = $filter->hasSplitWordsSearch() === false ? [$value] : explode(' ', $value); foreach ($words as $word) { - $like .= "$column LIKE ? OR "; + $like .= "$column LIKE ? $operator "; $args[] = "%$word%"; } } - $like = substr($like, 0, strlen($like) - 4) . ')'; + $like = substr($like, 0, strlen($like) - (strlen($operator) + 2)) . ')'; $or[] = $like; - $bigOr .= "$like OR "; + $bigOr .= "$like $operator "; $bigOrArgs = array_merge($bigOrArgs, $args); } if (sizeof($or) > 1) { - $bigOr = substr($bigOr, 0, strlen($bigOr) - 4) . ')'; + $bigOr = substr($bigOr, 0, strlen($bigOr) - (strlen($operator) + 2)) . ')'; $query = array_merge([$bigOr], $bigOrArgs); diff --git a/src/DataSource/NextrasDataSource.php b/src/DataSource/NextrasDataSource.php index c78127c7..275fed22 100755 --- a/src/DataSource/NextrasDataSource.php +++ b/src/DataSource/NextrasDataSource.php @@ -243,7 +243,7 @@ protected function applyFilterText(FilterText $filter): void // native handling with LikeFunction in v4 if (class_exists(LikeExpression::class)) { $conditions = [ - ICollection::OR, + $filter->hasConjunctionSearch() ? ICollection::AND : ICollection::OR, ]; foreach ($filter->getCondition() as $column => $value) { @@ -272,10 +272,11 @@ protected function applyFilterText(FilterText $filter): void $condition = $filter->getCondition(); $expr = '('; $params = []; + $operator = $filter->hasConjunctionSearch() ? 'AND' : 'OR'; foreach ($condition as $column => $value) { if ($filter->isExactSearch()) { - $expr .= '%column = %s OR '; + $expr .= "%column = %s $operator "; $params[] = $column; $params[] = "$value"; @@ -285,13 +286,13 @@ protected function applyFilterText(FilterText $filter): void $words = $filter->hasSplitWordsSearch() === false ? [$value] : explode(' ', $value); foreach ($words as $word) { - $expr .= '%column LIKE %s OR '; + $expr .= "%column LIKE %s $operator "; $params[] = $column; $params[] = "%$word%"; } } - $expr = preg_replace('/ OR $/', ')', $expr); + $expr = preg_replace("/ $operator $/", ')', $expr); array_unshift($params, $expr); diff --git a/src/Filter/FilterText.php b/src/Filter/FilterText.php index 3860d798..3d238539 100644 --- a/src/Filter/FilterText.php +++ b/src/Filter/FilterText.php @@ -30,6 +30,11 @@ class FilterText extends Filter */ protected $splitWordsSearch = true; + /** + * @var bool + */ + protected $conjunctionSearch = false; + /** * @var array|string[] */ @@ -110,4 +115,16 @@ public function hasSplitWordsSearch(): bool { return $this->splitWordsSearch; } + + + public function setConjunctionSearch(bool $conjunctionSearch = true): void + { + $this->conjunctionSearch = $conjunctionSearch; + } + + + public function hasConjunctionSearch(): bool + { + return $this->conjunctionSearch; + } } diff --git a/tests/Cases/DataSources/BaseDataSourceTest.phpt b/tests/Cases/DataSources/BaseDataSourceTest.phpt index d3adbe79..10910394 100644 --- a/tests/Cases/DataSources/BaseDataSourceTest.phpt +++ b/tests/Cases/DataSources/BaseDataSourceTest.phpt @@ -47,6 +47,11 @@ abstract class BaseDataSourceTest extends TestCase $this->ds->filter([$filter]); Assert::same(2, $this->ds->getCount()); + + $filter->setConjunctionSearch(); + + $this->ds->filter([$filter]); + Assert::same(1, $this->ds->getCount()); } public function testGetData(): void @@ -65,6 +70,13 @@ abstract class BaseDataSourceTest extends TestCase $this->data[0], $this->data[5], ], $this->getActualResultAsArray()); + + $filter->setConjunctionSearch(); + + $this->ds->filter([$filter]); + Assert::equal([ + $this->data[5], + ], $this->getActualResultAsArray()); } public function testFilterMultipleColumns(): void