diff --git a/src/Drivers/Standard/ParamsValidator.php b/src/Drivers/Standard/ParamsValidator.php index 766416d..66dac41 100644 --- a/src/Drivers/Standard/ParamsValidator.php +++ b/src/Drivers/Standard/ParamsValidator.php @@ -6,7 +6,6 @@ use Illuminate\Support\Facades\Validator; use Orion\Exceptions\MaxNestedDepthExceededException; -use Orion\Helpers\ArrayHelper; use Orion\Http\Requests\Request; use Orion\Http\Rules\WhitelistedField; use Orion\Http\Rules\WhitelistedQueryFields; @@ -109,7 +108,7 @@ public function validateSearch(Request $request): void public function validateAggregators(Request $request): void { - $depth = $this->nestedFiltersDepth($request->input('aggregates', []), -1); + $depth = $this->nestedFiltersDepth($request->input('aggregates', [])); Validator::make( $request->all(), @@ -194,7 +193,7 @@ public function validateAggregators(Request $request): void public function validateIncludes(Request $request): void { - $depth = $this->nestedFiltersDepth($request->input('includes', []), -1); + $depth = $this->nestedFiltersDepth($request->input('includes', [])); Validator::make( $request->all(), @@ -223,15 +222,25 @@ public function validateIncludes(Request $request): void /** * @throws MaxNestedDepthExceededException */ - protected function nestedFiltersDepth($array, $modifier = 0): int + protected function nestedFiltersDepth($array): int { - $depth = ArrayHelper::depth($array); - $configMaxNestedDepth = config('orion.search.max_nested_depth', 1); + $depth = 0; + + foreach ($array as $filterDescriptor) { + if (!isset($filterDescriptor['nested'])) { + continue; + } + + $currentFilterDescriptorDepth = 1 + $this->nestedFiltersDepth($filterDescriptor['nested']); - // Here we calculate the real nested filters depth - $depth = (int) floor($depth / 2); + if ($depth < $currentFilterDescriptorDepth) { + $depth = $currentFilterDescriptorDepth; + } + } + + $configMaxNestedDepth = config('orion.search.max_nested_depth', 1); - if ($depth + $modifier > $configMaxNestedDepth) { + if ($depth > $configMaxNestedDepth) { throw new MaxNestedDepthExceededException( 422, __('Max nested depth :depth is exceeded', ['depth' => $configMaxNestedDepth]) diff --git a/tests/Feature/StandardIndexNestedFilteringOperationsTest.php b/tests/Feature/StandardIndexNestedFilteringOperationsTest.php index 945d5db..6f843b4 100644 --- a/tests/Feature/StandardIndexNestedFilteringOperationsTest.php +++ b/tests/Feature/StandardIndexNestedFilteringOperationsTest.php @@ -4,12 +4,8 @@ namespace Orion\Tests\Feature; -use Carbon\Carbon; use Illuminate\Support\Facades\Gate; -use Orion\Tests\Fixtures\App\Models\Company; use Orion\Tests\Fixtures\App\Models\Post; -use Orion\Tests\Fixtures\App\Models\Team; -use Orion\Tests\Fixtures\App\Models\User; use Orion\Tests\Fixtures\App\Policies\GreenPolicy; class StandardIndexNestedFilteringOperationsTest extends TestCase @@ -26,11 +22,13 @@ public function getting_a_list_of_resources_nested_filtered_by_model_field_using '/api/posts/search', [ 'filters' => [ - ['field' => 'title', 'operator' => 'in' ,'value' => ['match', 'not_match']], - ['nested' => [ - ['field' => 'title', 'value' => 'match'], - ['field' => 'title', 'operator' => '!=', 'value' => 'not match'] - ]], + ['field' => 'title', 'operator' => 'in', 'value' => ['match', 'not_match']], + [ + 'nested' => [ + ['field' => 'title', 'value' => 'match'], + ['field' => 'title', 'operator' => '!=', 'value' => 'not match'], + ], + ], ], ] ); @@ -55,9 +53,12 @@ public function getting_a_list_of_resources_nested_filtered_by_model_field_using [ 'filters' => [ ['field' => 'title', 'operator' => '=', 'value' => 'match'], - ['type' => 'or', 'nested' => [ - ['field' => 'position', 'operator' => '=', 'value' => 3], - ]], + [ + 'type' => 'or', + 'nested' => [ + ['field' => 'position', 'operator' => '=', 'value' => 3], + ], + ], ], ] ); @@ -68,6 +69,36 @@ public function getting_a_list_of_resources_nested_filtered_by_model_field_using ); } + /** @test */ + public function getting_a_list_of_resources_nested_filtered_by_model_field_using_in_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', 'operator' => '!=', 'value' => 'not match'], + [ + 'nested' => [ + ['field' => 'title', 'operator' => 'in', 'value' => ['match']], + ['field' => 'title', 'operator' => '!=', 'value' => 'not match'], + ], + ], + ], + ] + ); + + $this->assertResourcesPaginated( + $response, + $this->makePaginator([$matchingPost], 'posts/search') + ); + } + /** @test */ public function getting_a_list_of_resources_nested_filtered_by_model_field_using_not_equal_operator(): void { @@ -80,9 +111,11 @@ public function getting_a_list_of_resources_nested_filtered_by_model_field_using '/api/posts/search', [ 'filters' => [ - ['nested' => [ - ['field' => 'position', 'operator' => '!=', 'value' => 5] - ]], + [ + 'nested' => [ + ['field' => 'position', 'operator' => '!=', 'value' => 5], + ], + ], ], ] ); @@ -105,9 +138,11 @@ public function getting_a_list_of_resources_nested_filtered_by_not_whitelisted_f '/api/posts/search', [ 'filters' => [ - ['nested' => [ - ['field' => 'body', 'operator' => '=', 'value' => 'match'] - ]], + [ + 'nested' => [ + ['field' => 'body', 'operator' => '=', 'value' => 'match'], + ], + ], ], ] );