diff --git a/docs-v2/content/en/search/advanced-filters.md b/docs-v2/content/en/search/advanced-filters.md index 0e9279f7..73604e0c 100644 --- a/docs-v2/content/en/search/advanced-filters.md +++ b/docs-v2/content/en/search/advanced-filters.md @@ -89,6 +89,22 @@ The frontend has to encode into base64 an array of filters. Each filter contains - `value` - this is optional, and represents the value the advanced filter will as a third argument in the `filter` method +## Apply advanced filters via POST Request (Version 9.3.0+) + +Starting from version 9.3.0, Laravel Restify introduces the ability to apply advanced filters using a POST request. This enhancement simplifies the process of sending complex filter payloads without the need for base64 encoding. Now, you can send the filters directly as JSON in the request body: + +```javascript +const filters = [ + { + 'key': 'ready-posts-filter', + 'value': null, + } +]; + +const response = await axios.post(`api/restify/posts/apply-restify-advanced-filters`, filters); +``` + + ### Custom uri key Since your class names could change along the way, you can define a `$uriKey` property to your filters, so the frontend will use always the same `key` when applying a filter: @@ -102,6 +118,64 @@ class ReadyPostsFilter extends AdvancedFilter }; ``` + +### Custom title + +```php +class ReadyPostsFilter extends AdvancedFilter +{ + public static $title = 'Ready to publish posts'; + + //... + +}; +``` + +### Custom description + +```php +class ReadyPostsFilter extends AdvancedFilter +{ + public static $description = 'Filter all posts that are ready to publish'; + + //... + +}; +``` + +### Custom meta + +```php +class ReadyPostsFilter extends AdvancedFilter +{ + public function meta(): array + { + return [ + 'icon' => 'icon', + 'color' => 'red', + 'operators' => [ + 'like' => 'Like', + 'eq' => 'Equal', + ] + ]; + } +}; +``` + +Meta will be rendered key/value in the frontend: + +```json +{ + ... + "icon": "icon", + "color": "red", + "operators": { + "like": "Like", + "eq": "Equal" + } +} +``` + ### Advanced filter value The third argument of the `filter` method is the raw value send by the frontend. Sometimes it might be an array, so you have to get the value using array access: @@ -392,16 +466,16 @@ In some scenarios, you might want to send additional data beyond the standard ke Consider the following payload: ```json -const filters = btoa(JSON.stringify([ +const filters = [ { 'key': ValueFilter::uriKey(), 'value': 'Valid%', 'operator' => 'like', 'column' => 'description', } -])); +]; -const response = await axios.get(`api/restify/posts?filters=${filters}`); +const response = await axios.post(`api/restify/posts/apply-restify-advanced-filters`, filters); ``` In this payload, besides the standard key and value, we are also sending operator and column. The operator specifies the type of SQL operation, and the column specifies the database column to filter. diff --git a/src/Bootstrap/RoutesDefinition.php b/src/Bootstrap/RoutesDefinition.php index a9c0cbe1..895eed1e 100644 --- a/src/Bootstrap/RoutesDefinition.php +++ b/src/Bootstrap/RoutesDefinition.php @@ -22,6 +22,11 @@ public function __invoke(?string $uriKey = null) \Binaryk\LaravelRestify\Http\Controllers\RepositoryFilterController::class )->name('filters.index'); + Route::post( + $prefix.'/apply-restify-advanced-filters', + \Binaryk\LaravelRestify\Http\Controllers\RepositoryApplyFiltersController::class + )->name('filters.apply'); + // Actions Route::get( $prefix.'/actions', diff --git a/src/Events/AdvancedFiltersApplied.php b/src/Events/AdvancedFiltersApplied.php index bb1fd041..e2167af8 100644 --- a/src/Events/AdvancedFiltersApplied.php +++ b/src/Events/AdvancedFiltersApplied.php @@ -10,6 +10,6 @@ class AdvancedFiltersApplied public function __construct( public Repository $repository, public AdvancedFiltersCollection $advancedFiltersCollection, - public ?string $rawFilters = null, + public array $rawFilters = [], ) {} } diff --git a/src/Filters/AdvancedFiltersCollection.php b/src/Filters/AdvancedFiltersCollection.php index bd276142..969bf96d 100644 --- a/src/Filters/AdvancedFiltersCollection.php +++ b/src/Filters/AdvancedFiltersCollection.php @@ -26,12 +26,10 @@ public function apply(RestifyRequest $request, $query): self public static function collectQueryFilters(RestifyRequest $request, Repository $repository): self { - if (! $request->input('filters')) { + if (! $filters = $request->filters()) { return static::make([]); } - $filters = json_decode(base64_decode($request->input('filters')), true); - $allowedFilters = $repository->collectAdvancedFilters($request); return static::make($filters) diff --git a/src/Http/Controllers/RepositoryApplyFiltersController.php b/src/Http/Controllers/RepositoryApplyFiltersController.php new file mode 100644 index 00000000..47207885 --- /dev/null +++ b/src/Http/Controllers/RepositoryApplyFiltersController.php @@ -0,0 +1,13 @@ +repository()->index($request); + } +} diff --git a/src/Http/Requests/RepositoryApplyFiltersRequest.php b/src/Http/Requests/RepositoryApplyFiltersRequest.php new file mode 100644 index 00000000..efce871a --- /dev/null +++ b/src/Http/Requests/RepositoryApplyFiltersRequest.php @@ -0,0 +1,5 @@ +input('filters', []) + : (json_decode(base64_decode($this->input('filters')), true) ?? []); + } } diff --git a/src/Services/Search/RepositorySearchService.php b/src/Services/Search/RepositorySearchService.php index afea043e..aecee850 100644 --- a/src/Services/Search/RepositorySearchService.php +++ b/src/Services/Search/RepositorySearchService.php @@ -210,7 +210,7 @@ protected function applyFilters(RestifyRequest $request, Repository $repository, $repository, AdvancedFiltersCollection::collectQueryFilters($request, $repository) ->apply($request, $query), - $request->input('filters'), + $request->filters(), ) ); diff --git a/tests/Feature/Filters/AdvancedFilterTest.php b/tests/Feature/Filters/AdvancedFilterTest.php index bb46986b..96d0dcdc 100644 --- a/tests/Feature/Filters/AdvancedFilterTest.php +++ b/tests/Feature/Filters/AdvancedFilterTest.php @@ -242,4 +242,30 @@ public function test_filter_can_send_meta(): void 'filters' => $filters, ]))->assertJsonCount(0, 'data'); } + + public function test_filter_can_be_sent_in_post_requests(): void + { + Post::factory()->create([ + 'title' => 'Valid post', + 'description' => 'Zoo bar post', + ]); + + Post::factory()->create([ + 'title' => 'Active post', + 'description' => 'Foo bar post', + ]); + + $filters = [ + [ + 'key' => ValueFilter::uriKey(), + 'value' => 'Valid%', + 'operator' => 'like', + 'column' => 'title', + ], + ]; + + $this->post(PostRepository::route('apply-restify-advanced-filters'), [ + 'filters' => $filters, + ])->assertJsonCount(1, 'data'); + } }