diff --git a/src/Traits/SearchTrait.php b/src/Traits/SearchTrait.php index 0812eb4a..64145a06 100644 --- a/src/Traits/SearchTrait.php +++ b/src/Traits/SearchTrait.php @@ -57,19 +57,24 @@ public function paginate(): LengthAwarePaginator */ public function filterBy(string $field, ?string $filterName = null): self { - if (empty($filterName)) { - if (Str::contains($field, '.')) { - list ($filterName) = extract_last_part($field); - } else { - $filterName = $field; - } - } + $filterName ??= $this->getFilterName($field); if (Arr::has($this->filter, $filterName)) { - $values = Arr::wrap($this->filter[$filterName]); + $this->applyWhereCallback($this->query, $field, function (&$query, $conditionField) use ($filterName) { + $query->where($conditionField, $this->filter[$filterName]); + }); + } + + return $this; + } + + public function filterByList(string $field, ?string $filterName = null): self + { + $filterName ??= $this->getFilterName($field); - $this->applyWhereCallback($this->query, $field, function (&$query, $conditionField) use ($values) { - $query->whereIn($conditionField, $values); + if (Arr::has($this->filter, $filterName)) { + $this->applyWhereCallback($this->query, $field, function (&$query, $conditionField) use ($filterName) { + $query->whereIn($conditionField, $this->filter[$filterName]); }); } @@ -145,7 +150,7 @@ public function searchQuery(array $filter = []): self $this->filterTo($field, false, $fieldName); } elseif (Str::endsWith($fieldName, '_in_list')) { $field = Str::replace('_in_list', '', $fieldName); - $this->query->whereIn($field, $value); + $this->filterByList($field, $fieldName); } else { $this->filterBy($fieldName); } @@ -385,4 +390,13 @@ protected function postQueryHook(): void $this->withCount([]); } } + + protected function getFilterName(string $field): string + { + if (Str::contains($field, '.')) { + [$field] = extract_last_part($field); + } + + return $field; + } } diff --git a/tests/SearchTraitTest.php b/tests/SearchTraitTest.php index 77b80bbc..50007ec8 100644 --- a/tests/SearchTraitTest.php +++ b/tests/SearchTraitTest.php @@ -253,6 +253,44 @@ public function testSearchQueryWithFilters() ->getSearchResults(); } + public function testSearchQueryWithNullFilters() + { + $this->mockSelectWithAggregate( + 'select count(*) as aggregate from `test_models` where `user_id` is null and `test_models`.`deleted_at` is null' + ); + + $this->mockSelect( + 'select * from `test_models` where `user_id` is null and `test_models`.`deleted_at` is null order by `id` asc limit 15 offset 0' + ); + + $this->testRepositoryClass + ->searchQuery(['user_id' => null]) + ->getSearchResults(); + } + + public function testSearchQueryWithListFilters() + { + $this->setAdditionalReservedFiltersMethod->invokeArgs($this->testRepositoryClass, [ + 'user_id' + ]); + + $this->mockSelectWithAggregate( + 'select count(*) as aggregate from `test_models` where `user_id` in (?, ?, ?) and `test_models`.`deleted_at` is null', + [1, 2, 3] + ); + + $this->mockSelect( + 'select * from `test_models` where `user_id` in (?, ?, ?) and `test_models`.`deleted_at` is null order by `id` asc limit 15 offset 0', + [], + [1, 2, 3] + ); + + $this->testRepositoryClass + ->searchQuery(['user_id' => [1, 2, 3]]) + ->filterByList('user_id') + ->getSearchResults(); + } + public function testSearchQueryWithFiltersFunctions() { $this->shouldSettablePropertiesBeResetProperty->setValue($this->testRepositoryClass, false); @@ -273,4 +311,4 @@ public function testSearchQueryWithFiltersFunctions() ->filterLessThan('updated_at', Carbon::now()) ->getSearchResults(); } -} \ No newline at end of file +} diff --git a/tests/support/Traits/SqlMockTrait.php b/tests/support/Traits/SqlMockTrait.php index a1144f6d..48f96eab 100644 --- a/tests/support/Traits/SqlMockTrait.php +++ b/tests/support/Traits/SqlMockTrait.php @@ -346,7 +346,7 @@ protected function mockGetSearchResultWithRelations(array $selectResult): void . "where ((`query_field` like '%search_string%') or exists (select * from `relation_models` " . "where `test_models`.`id` = `relation_models`.`test_model_id` " . "and (`another_query_field` like '%search_string%'))) and exists (select * from `relation_models` " - . "where `test_models`.`id` = `relation_models`.`test_model_id` and `name` in (?)) " + . "where `test_models`.`id` = `relation_models`.`test_model_id` and `name` = ?) " . "and `test_models`.`deleted_at` is null", ['some_value'] ); @@ -359,7 +359,7 @@ protected function mockGetSearchResultWithRelations(array $selectResult): void . "where `test_models`.`id` = `relation_models`.`test_model_id` " . "and (`another_query_field` like '%search_string%'))) and " . "exists (select * from `relation_models` where `test_models`.`id` = `relation_models`.`test_model_id` " - . "and `name` in (?)) and `test_models`.`deleted_at` is null " + . "and `name` = ?) and `test_models`.`deleted_at` is null " . "order by `relation_id` asc, `id` asc limit 15 offset 0", $selectResult, ['some_value'], @@ -370,7 +370,7 @@ protected function mockGetSearchResultWithFilters(array $selectResult): void { $this->mockSelectWithAggregate( "select count(*) as aggregate from `test_models` where `user_id` in (?, ?) and `user_id` " - . "not in (?, ?) and `name` in (?) and `date` >= ? and `date` <= ? " + . "not in (?, ?) and `name` = ? and `date` >= ? and `date` <= ? " . "and `created_at` >= ? and `created_at` <= ? and `updated_at` > ? " . "and `updated_at` < ? and `test_models`.`deleted_at` is null", [ @@ -390,7 +390,7 @@ protected function mockGetSearchResultWithFilters(array $selectResult): void $this->mockSelect( "select * from `test_models` where `user_id` in (?, ?) and `user_id` not in (?, ?) " - . "and `name` in (?) and `date` >= ? and `date` <= ? and `created_at` >= ? and `created_at` <= ? " + . "and `name` = ? and `date` >= ? and `date` <= ? and `created_at` >= ? and `created_at` <= ? " . "and `updated_at` > ? and `updated_at` < ? and `test_models`.`deleted_at` is null " . "order by `id` asc limit 15 offset 0", $selectResult, @@ -500,4 +500,4 @@ protected function getPdo(): SingleConnectionProxy return $this->pdo; } -} \ No newline at end of file +}