diff --git a/composer.json b/composer.json index 61925bf6..8be4f01e 100644 --- a/composer.json +++ b/composer.json @@ -30,8 +30,8 @@ }, "require-dev": { "mockery/mockery": "^1.0", - "phpunit/phpunit": "^6.0|^7.0|^8.0|^9.0", - "orchestra/testbench": "^3.7|^4.0|^5.0|^6.0" + "orchestra/testbench": "^3.7|^4.0|^5.0|^6.0", + "phpunit/phpunit": "^6.0|^7.0|^8.0|^9.0" }, "autoload": { "psr-4": { diff --git a/src/Drivers/Standard/ParamsValidator.php b/src/Drivers/Standard/ParamsValidator.php index 6c67954d..684c1df2 100644 --- a/src/Drivers/Standard/ParamsValidator.php +++ b/src/Drivers/Standard/ParamsValidator.php @@ -53,7 +53,7 @@ public function validateFilters(Request $request): void 'filters' => ['sometimes', 'array'], 'filters.*.type' => ['sometimes', 'in:and,or'], 'filters.*.field' => ['required_with:filters', 'regex:/^[\w.\_\-\>]+$/', new WhitelistedField($this->filterableBy)], - 'filters.*.operator' => ['required_with:filters', 'in:<,<=,>,>=,=,!=,like,not like,ilike,not ilike,in,not in'], + 'filters.*.operator' => ['sometimes', 'in:<,<=,>,>=,=,!=,like,not like,ilike,not ilike,in,not in'], 'filters.*.value' => ['present', 'nullable'], ] )->validate(); diff --git a/src/Drivers/Standard/QueryBuilder.php b/src/Drivers/Standard/QueryBuilder.php index ffb4b2b3..66e8bd5a 100644 --- a/src/Drivers/Standard/QueryBuilder.php +++ b/src/Drivers/Standard/QueryBuilder.php @@ -187,7 +187,7 @@ protected function buildFilterNestedQueryWhereClause( if (!is_array($filterDescriptor['value']) || $constraint === 'whereDate') { $query->{$or ? 'or' . ucfirst($constraint) : $constraint}( $field, - $filterDescriptor['operator'], + $filterDescriptor['operator'] ?? '=', $filterDescriptor['value'] ); } else { @@ -258,14 +258,14 @@ protected function buildPivotFilterNestedQueryWhereClause( $query->addNestedWhereQuery( $query->newPivotStatement()->whereDate( $query->getTable().".{$field}", - $filterDescriptor['operator'], + $filterDescriptor['operator'] ?? '=', $filterDescriptor['value'] ) ); } elseif (!is_array($filterDescriptor['value'])) { $query->{$or ? 'orWherePivot' : 'wherePivot'}( $field, - $filterDescriptor['operator'], + $filterDescriptor['operator'] ?? '=', $filterDescriptor['value'] ); } else { diff --git a/src/Specs/Builders/Partials/RequestBody/Search/FiltersBuilder.php b/src/Specs/Builders/Partials/RequestBody/Search/FiltersBuilder.php index d79722e8..39e54fae 100644 --- a/src/Specs/Builders/Partials/RequestBody/Search/FiltersBuilder.php +++ b/src/Specs/Builders/Partials/RequestBody/Search/FiltersBuilder.php @@ -36,7 +36,7 @@ public function build(): ?array ] ], 'required' => [ - 'field', 'operator', 'value' + 'field', 'value' ] ] ]; diff --git a/tests/Feature/StandardIndexFilteringOperationsTest.php b/tests/Feature/StandardIndexFilteringOperationsTest.php index 1e3f9a5a..01cc02d2 100644 --- a/tests/Feature/StandardIndexFilteringOperationsTest.php +++ b/tests/Feature/StandardIndexFilteringOperationsTest.php @@ -12,6 +12,29 @@ class StandardIndexFilteringOperationsTest extends TestCase { + /** @test */ + public function getting_a_list_of_resources_filtered_by_model_field_using_default_operator(): void + { + $matchingPost = factory(Post::class)->create(['title' => 'match'])->fresh(); + factory(Post::class)->create(['title' => 'not match'])->fresh(); + + Gate::policy(Post::class, GreenPolicy::class); + + $response = $this->post( + '/api/posts/search', + [ + 'filters' => [ + ['field' => 'title', 'value' => 'match'], + ], + ] + ); + + $this->assertResourcesPaginated( + $response, + $this->makePaginator([$matchingPost], 'posts/search') + ); + } + /** @test */ public function getting_a_list_of_resources_filtered_by_model_field_using_equal_operator(): void {