From 13755cda9bb3edfbbdb1c01a5a36436685a8dca2 Mon Sep 17 00:00:00 2001 From: Danilo Pinotti Date: Mon, 31 Oct 2022 12:53:01 -0300 Subject: [PATCH 1/6] isolate "where" applying --- src/Filters/FiltersPartial.php | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/Filters/FiltersPartial.php b/src/Filters/FiltersPartial.php index 116be6d7..c2670629 100644 --- a/src/Filters/FiltersPartial.php +++ b/src/Filters/FiltersPartial.php @@ -21,20 +21,15 @@ public function __invoke(Builder $query, $value, string $property) } } - $wrappedProperty = $query->getQuery()->getGrammar()->wrap($query->qualifyColumn($property)); - - $sql = "LOWER({$wrappedProperty}) LIKE ?"; - if (is_array($value)) { if (count(array_filter($value, 'strlen')) === 0) { return $query; } - $query->where(function (Builder $query) use ($value, $sql) { + $query->where(function (Builder $query) use ($value, $sql, $property) { foreach (array_filter($value, 'strlen') as $partialValue) { $partialValue = mb_strtolower($partialValue, 'UTF8'); - - $query->orWhereRaw($sql, ["%{$partialValue}%"]); + $this->applyWhere($query, $partialValue, $property); } }); @@ -43,6 +38,13 @@ public function __invoke(Builder $query, $value, string $property) $value = mb_strtolower($value, 'UTF8'); - $query->whereRaw($sql, ["%{$value}%"]); + $this->applyWhere($query, $value, $property); + } + + protected function applyWhere(Builder $query, $value, string $property) + { + $wrappedProperty = $query->getQuery()->getGrammar()->wrap($query->qualifyColumn($property)); + + $query->whereRaw("LOWER({$wrappedProperty}) LIKE ?", ["%{$value}%"]); } } From 4948d185f0a4d1683a3d469dbaffc7ee1859fc30 Mon Sep 17 00:00:00 2001 From: Danilo Pinotti Date: Mon, 31 Oct 2022 12:55:58 -0300 Subject: [PATCH 2/6] use strtolower when applying "where" --- src/Filters/FiltersPartial.php | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/Filters/FiltersPartial.php b/src/Filters/FiltersPartial.php index c2670629..3b0e7171 100644 --- a/src/Filters/FiltersPartial.php +++ b/src/Filters/FiltersPartial.php @@ -28,7 +28,6 @@ public function __invoke(Builder $query, $value, string $property) $query->where(function (Builder $query) use ($value, $sql, $property) { foreach (array_filter($value, 'strlen') as $partialValue) { - $partialValue = mb_strtolower($partialValue, 'UTF8'); $this->applyWhere($query, $partialValue, $property); } }); @@ -36,13 +35,13 @@ public function __invoke(Builder $query, $value, string $property) return; } - $value = mb_strtolower($value, 'UTF8'); - $this->applyWhere($query, $value, $property); } protected function applyWhere(Builder $query, $value, string $property) { + $value = mb_strtolower($value, 'UTF8'); + $wrappedProperty = $query->getQuery()->getGrammar()->wrap($query->qualifyColumn($property)); $query->whereRaw("LOWER({$wrappedProperty}) LIKE ?", ["%{$value}%"]); From bc4c8acb44a8d852c89908c02b63184a95623b5a Mon Sep 17 00:00:00 2001 From: Danilo Pinotti Date: Mon, 31 Oct 2022 13:00:33 -0300 Subject: [PATCH 3/6] add beginsWithStrict filter --- src/AllowedFilter.php | 8 ++++++++ src/Filters/FiltersBeginsWithStrict.php | 19 +++++++++++++++++++ 2 files changed, 27 insertions(+) create mode 100644 src/Filters/FiltersBeginsWithStrict.php diff --git a/src/AllowedFilter.php b/src/AllowedFilter.php index 03e7866c..bf9d3be8 100644 --- a/src/AllowedFilter.php +++ b/src/AllowedFilter.php @@ -4,6 +4,7 @@ use Illuminate\Support\Collection; use Spatie\QueryBuilder\Filters\Filter; +use Spatie\QueryBuilder\Filters\FiltersBeginsWithStrict; use Spatie\QueryBuilder\Filters\FiltersCallback; use Spatie\QueryBuilder\Filters\FiltersExact; use Spatie\QueryBuilder\Filters\FiltersPartial; @@ -70,6 +71,13 @@ public static function partial(string $name, $internalName = null, bool $addRela return new static($name, new FiltersPartial($addRelationConstraint), $internalName); } + public static function beginsWithStrict(string $name, $internalName = null, bool $addRelationConstraint = true, string $arrayValueDelimiter = null): self + { + static::setFilterArrayValueDelimiter($arrayValueDelimiter); + + return new static($name, new FiltersBeginsWithStrict($addRelationConstraint), $internalName); + } + public static function scope(string $name, $internalName = null, string $arrayValueDelimiter = null): self { static::setFilterArrayValueDelimiter($arrayValueDelimiter); diff --git a/src/Filters/FiltersBeginsWithStrict.php b/src/Filters/FiltersBeginsWithStrict.php new file mode 100644 index 00000000..388fd78f --- /dev/null +++ b/src/Filters/FiltersBeginsWithStrict.php @@ -0,0 +1,19 @@ + + */ +class FiltersBeginsWithStrict extends FiltersPartial implements Filter +{ + protected function applyWhere(Builder $query, $value, string $property) + { + $wrappedProperty = $query->getQuery()->getGrammar()->wrap($query->qualifyColumn($property)); + + $query->whereRaw("{$wrappedProperty} LIKE ?", ["{$value}%"]); + } +} From 00623ecf4e73c84fd5ade8d1556a22b455017e4c Mon Sep 17 00:00:00 2001 From: Danilo Pinotti Date: Mon, 31 Oct 2022 13:11:33 -0300 Subject: [PATCH 4/6] remove unused variable --- src/Filters/FiltersPartial.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Filters/FiltersPartial.php b/src/Filters/FiltersPartial.php index 3b0e7171..d42512c1 100644 --- a/src/Filters/FiltersPartial.php +++ b/src/Filters/FiltersPartial.php @@ -26,7 +26,7 @@ public function __invoke(Builder $query, $value, string $property) return $query; } - $query->where(function (Builder $query) use ($value, $sql, $property) { + $query->where(function (Builder $query) use ($value, $property) { foreach (array_filter($value, 'strlen') as $partialValue) { $this->applyWhere($query, $partialValue, $property); } From 8b882b45397ae7071834ff89cbedbe8550c88bfa Mon Sep 17 00:00:00 2001 From: Danilo Pinotti Date: Tue, 1 Nov 2022 12:39:38 -0300 Subject: [PATCH 5/6] implements getWhereRawParameters and use it instead applyWhere --- src/Filters/FiltersBeginsWithStrict.php | 9 +++++---- src/Filters/FiltersPartial.php | 19 ++++++++++++------- 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/src/Filters/FiltersBeginsWithStrict.php b/src/Filters/FiltersBeginsWithStrict.php index 388fd78f..03d18ecb 100644 --- a/src/Filters/FiltersBeginsWithStrict.php +++ b/src/Filters/FiltersBeginsWithStrict.php @@ -10,10 +10,11 @@ */ class FiltersBeginsWithStrict extends FiltersPartial implements Filter { - protected function applyWhere(Builder $query, $value, string $property) + protected function getWhereRawParameters($value, string $property): array { - $wrappedProperty = $query->getQuery()->getGrammar()->wrap($query->qualifyColumn($property)); - - $query->whereRaw("{$wrappedProperty} LIKE ?", ["{$value}%"]); + return [ + "{$property} LIKE ?", + ["{$value}%"], + ]; } } diff --git a/src/Filters/FiltersPartial.php b/src/Filters/FiltersPartial.php index d42512c1..4aad8560 100644 --- a/src/Filters/FiltersPartial.php +++ b/src/Filters/FiltersPartial.php @@ -21,29 +21,34 @@ public function __invoke(Builder $query, $value, string $property) } } + $wrappedProperty = $query->getQuery()->getGrammar()->wrap($query->qualifyColumn($property)); + if (is_array($value)) { if (count(array_filter($value, 'strlen')) === 0) { return $query; } - $query->where(function (Builder $query) use ($value, $property) { + $query->where(function (Builder $query) use ($value, $wrappedProperty) { foreach (array_filter($value, 'strlen') as $partialValue) { - $this->applyWhere($query, $partialValue, $property); + [$sql, $bindings] = $this->getWhereRawParameters($partialValue, $wrappedProperty); + $query->orWhereRaw($sql, $bindings); } }); return; } - $this->applyWhere($query, $value, $property); + [$sql, $bindings] = $this->getWhereRawParameters($value, $wrappedProperty); + $query->whereRaw($sql, $bindings); } - protected function applyWhere(Builder $query, $value, string $property) + protected function getWhereRawParameters($value, string $property): array { $value = mb_strtolower($value, 'UTF8'); - $wrappedProperty = $query->getQuery()->getGrammar()->wrap($query->qualifyColumn($property)); - - $query->whereRaw("LOWER({$wrappedProperty}) LIKE ?", ["%{$value}%"]); + return [ + "LOWER({$property}) LIKE ?", + ["%{$value}%"], + ]; } } From b9e69c7a9b4d6acf3e7ca7103f4ac143251befd6 Mon Sep 17 00:00:00 2001 From: Danilo Pinotti Date: Tue, 1 Nov 2022 13:05:58 -0300 Subject: [PATCH 6/6] add tests --- tests/FilterTest.php | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/tests/FilterTest.php b/tests/FilterTest.php index 5a0030f2..abb325a6 100644 --- a/tests/FilterTest.php +++ b/tests/FilterTest.php @@ -130,6 +130,37 @@ $this->assertQueryLogContains("select * from `test_models` where (LOWER(`test_models`.`id`) LIKE ?)"); }); +test('falsy values are not ignored when applying a begins with strict filter', function () { + DB::enableQueryLog(); + + createQueryFromFilterRequest([ + 'id' => [0], + ]) + ->allowedFilters(AllowedFilter::beginsWithStrict('id')) + ->get(); + + $this->assertQueryLogContains("select * from `test_models` where (`test_models`.`id` LIKE ?)"); +}); + +it('can filter partial using begins with strict', function () { + TestModel::create([ + 'name' => 'John Doe', + ]); + + $models = createQueryFromFilterRequest(['name' => 'john']) + ->allowedFilters([ + AllowedFilter::beginsWithStrict('name'), + ]); + + $models2 = createQueryFromFilterRequest(['name' => 'doe']) + ->allowedFilters([ + AllowedFilter::beginsWithStrict('name'), + ]); + + expect($models->count())->toBe(1); + expect($models2->count())->toBe(0); +}); + it('can filter and match results by exact property', function () { $testModel = TestModel::first();