From 3e607d7fe1b69451efadc18bdbe52fdd63a92a36 Mon Sep 17 00:00:00 2001 From: Eduard Lupacescu Date: Mon, 17 Nov 2025 09:52:54 +0200 Subject: [PATCH 1/5] feat: adding the filtered query builder as a second argument in props of getters --- src/Getters/Getter.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Getters/Getter.php b/src/Getters/Getter.php index 72003fa6..c151da8a 100644 --- a/src/Getters/Getter.php +++ b/src/Getters/Getter.php @@ -6,6 +6,7 @@ use Binaryk\LaravelRestify\Http\Requests\RestifyRequest; use Binaryk\LaravelRestify\MCP\Actions\JsonSchemaFromRulesAction; use Binaryk\LaravelRestify\Restify; +use Binaryk\LaravelRestify\Services\Search\RepositorySearchService; use Binaryk\LaravelRestify\Traits\AuthorizedToRun; use Binaryk\LaravelRestify\Traits\AuthorizedToSee; use Binaryk\LaravelRestify\Traits\Make; @@ -116,7 +117,9 @@ public function handleRequest(GetterRequest $request): Response ); } - return $this->handle($request); + $query = RepositorySearchService::make()->search($request, $request->repository()); + + return $this->handle($request, $query); } public function withoutMiddleware(string|array $middleware): self From ffad0c51fc5d7ca3f4175dda8b14f24d45ec2848 Mon Sep 17 00:00:00 2001 From: binaryk Date: Mon, 17 Nov 2025 07:53:20 +0000 Subject: [PATCH 2/5] Fix styling --- src/MCP/Bootstrap/BootMcpTools.php | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/MCP/Bootstrap/BootMcpTools.php b/src/MCP/Bootstrap/BootMcpTools.php index 401c66cc..8beadc2e 100644 --- a/src/MCP/Bootstrap/BootMcpTools.php +++ b/src/MCP/Bootstrap/BootMcpTools.php @@ -140,8 +140,8 @@ protected function discoverCustomTools(): Collection protected function discoverRepositoryTools(): Collection { return collect(Restify::$repositories) - ->filter(fn(string $repo): bool => in_array(HasMcpTools::class, class_uses_recursive($repo))) - ->flatMap(fn(string $repoClass): Collection => $this->discoverRepositoryOperations($repoClass)) + ->filter(fn (string $repo): bool => in_array(HasMcpTools::class, class_uses_recursive($repo))) + ->flatMap(fn (string $repoClass): Collection => $this->discoverRepositoryOperations($repoClass)) ->values(); } @@ -211,10 +211,10 @@ protected function discoverActions(string $repositoryClass, Repository $reposito $actionRequest = app(McpActionRequest::class); return $repository->resolveActions($actionRequest) - ->filter(fn($action): bool => $action instanceof Action) - ->filter(fn(Action $action): bool => $action->isShownOnMcp($actionRequest, $repository)) - ->filter(fn(Action $action): bool => $action->authorizedToSee($actionRequest)) - ->unique(fn(Action $action): string => $action->uriKey()) + ->filter(fn ($action): bool => $action instanceof Action) + ->filter(fn (Action $action): bool => $action->isShownOnMcp($actionRequest, $repository)) + ->filter(fn (Action $action): bool => $action->authorizedToSee($actionRequest)) + ->unique(fn (Action $action): string => $action->uriKey()) ->map(function (Action $action) use ($repositoryClass, $repository): array { $instance = new ActionTool($repositoryClass, $action); @@ -241,10 +241,10 @@ protected function discoverGetters(string $repositoryClass, Repository $reposito $getterRequest = app(McpGetterRequest::class); return $repository->resolveGetters($getterRequest) - ->filter(fn($getter): bool => $getter instanceof Getter) - ->filter(fn(Getter $getter): bool => $getter->isShownOnMcp($getterRequest, $repository)) - ->filter(fn(Getter $getter): bool => $getter->authorizedToSee($getterRequest)) - ->unique(fn(Getter $getter): string => $getter->uriKey()) + ->filter(fn ($getter): bool => $getter instanceof Getter) + ->filter(fn (Getter $getter): bool => $getter->isShownOnMcp($getterRequest, $repository)) + ->filter(fn (Getter $getter): bool => $getter->authorizedToSee($getterRequest)) + ->unique(fn (Getter $getter): string => $getter->uriKey()) ->map(function (Getter $getter) use ($repositoryClass, $repository): array { $instance = new GetterTool($repositoryClass, $getter); From d99ae6251d474d29a6ead0829bb15cf06f612673 Mon Sep 17 00:00:00 2001 From: Eduard Lupacescu Date: Mon, 17 Nov 2025 10:10:52 +0200 Subject: [PATCH 3/5] fix: adding query filtered to request --- docs-v3/content/docs/api/getters.md | 35 +++++++++++++++- src/Getters/Getter.php | 4 +- .../Concerns/InteractWithRepositories.php | 6 +++ .../Post/Getters/PostsFilteredQueryGetter.php | 24 +++++++++++ tests/Fixtures/Post/PostRepository.php | 2 + tests/Getters/PerformGetterControllerTest.php | 42 +++++++++++++++++++ 6 files changed, 108 insertions(+), 5 deletions(-) create mode 100644 tests/Fixtures/Post/Getters/PostsFilteredQueryGetter.php diff --git a/docs-v3/content/docs/api/getters.md b/docs-v3/content/docs/api/getters.md index 5f4959a7..3045c8e4 100644 --- a/docs-v3/content/docs/api/getters.md +++ b/docs-v3/content/docs/api/getters.md @@ -143,6 +143,27 @@ public function handle(Request $request) } ``` +#### Accessing the filtered query + +For index getters, you can access the filtered query builder via `$request->filteredQuery()`. This query builder already has all filters, search queries, and other query modifiers applied by Restify: + +```php +public function handle(Request $request): JsonResponse +{ + // Get the filtered query builder with all applied filters, search, etc. + $query = $request->filteredQuery(); + + // You can further refine the query + $data = $query->where('status', 'active')->get(); + + return response()->json([ + 'data' => $data, + ]); +} +``` + +This allows you to build upon the existing query without having to manually apply filters again. + ## Getter customizations Getters could be easily customized. @@ -278,16 +299,26 @@ Index getters are used when you have to apply them for many items. ### Index getter definition -The index getter definition differs in how it receives arguments for the `handle` method. +The index getter definition receives only the `$request` in the `handle` method. You can access the filtered query builder using `$request->filteredQuery()`: ```php public function handle(Request $request): JsonResponse { - // + // Get the filtered query builder with all applied filters, search, etc. + $query = $request->filteredQuery(); + + // You can further refine the query + $data = $query->where('status', 'active')->get(); + + return response()->json([ + 'data' => $data, + ]); } ``` +The filtered query builder contains all repository filters, search queries, and other query modifiers already applied. This allows you to leverage existing filters without re-implementing them. + ### Index getter registration To register an index getter, we have to use the `->onlyOnIndex()` accessor: diff --git a/src/Getters/Getter.php b/src/Getters/Getter.php index c151da8a..7abda010 100644 --- a/src/Getters/Getter.php +++ b/src/Getters/Getter.php @@ -117,9 +117,7 @@ public function handleRequest(GetterRequest $request): Response ); } - $query = RepositorySearchService::make()->search($request, $request->repository()); - - return $this->handle($request, $query); + return $this->handle($request); } public function withoutMiddleware(string|array $middleware): self diff --git a/src/Http/Requests/Concerns/InteractWithRepositories.php b/src/Http/Requests/Concerns/InteractWithRepositories.php index 100357ac..a793f866 100644 --- a/src/Http/Requests/Concerns/InteractWithRepositories.php +++ b/src/Http/Requests/Concerns/InteractWithRepositories.php @@ -7,6 +7,7 @@ use Binaryk\LaravelRestify\Repositories\Repository; use Binaryk\LaravelRestify\Repositories\RepositoryInstance; use Binaryk\LaravelRestify\Restify; +use Binaryk\LaravelRestify\Services\Search\RepositorySearchService; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\Relation; @@ -121,4 +122,9 @@ public function isViaRepository(): bool return $parentRepository && $parentRepositoryId; } + + public function filteredQuery(): Builder + { + return RepositorySearchService::make()->search($this, $this->repository()); + } } diff --git a/tests/Fixtures/Post/Getters/PostsFilteredQueryGetter.php b/tests/Fixtures/Post/Getters/PostsFilteredQueryGetter.php new file mode 100644 index 00000000..532b9c4a --- /dev/null +++ b/tests/Fixtures/Post/Getters/PostsFilteredQueryGetter.php @@ -0,0 +1,24 @@ +filteredQuery(); + + $count = $query->count(); + + return response()->json([ + 'message' => 'filtered query works', + 'count' => $count, + ]); + } +} diff --git a/tests/Fixtures/Post/PostRepository.php b/tests/Fixtures/Post/PostRepository.php index 1bcf4f82..7fee7c03 100644 --- a/tests/Fixtures/Post/PostRepository.php +++ b/tests/Fixtures/Post/PostRepository.php @@ -7,6 +7,7 @@ use Binaryk\LaravelRestify\Http\Requests\ActionRequest; use Binaryk\LaravelRestify\Http\Requests\RestifyRequest; use Binaryk\LaravelRestify\Repositories\Repository; +use Binaryk\LaravelRestify\Tests\Fixtures\Post\Getters\PostsFilteredQueryGetter; use Binaryk\LaravelRestify\Tests\Fixtures\Post\Getters\PostsIndexGetter; use Binaryk\LaravelRestify\Tests\Fixtures\Post\Getters\PostsIndexInvokableGetter; use Binaryk\LaravelRestify\Tests\Fixtures\Post\Getters\PostsShowGetter; @@ -139,6 +140,7 @@ public function getters(RestifyRequest $request): array PostsIndexGetter::make(), PostsShowGetter::make()->onlyOnShow(), UnauthenticatedActionGetter::make()->withoutMiddleware(AuthorizeRestify::class), + PostsFilteredQueryGetter::make(), new PostsShowInvokableGetter, new PostsIndexInvokableGetter, ]; diff --git a/tests/Getters/PerformGetterControllerTest.php b/tests/Getters/PerformGetterControllerTest.php index 5762b54e..21bf0c68 100644 --- a/tests/Getters/PerformGetterControllerTest.php +++ b/tests/Getters/PerformGetterControllerTest.php @@ -2,10 +2,12 @@ namespace Binaryk\LaravelRestify\Tests\Getters; +use Binaryk\LaravelRestify\Tests\Fixtures\Post\Getters\PostsFilteredQueryGetter; use Binaryk\LaravelRestify\Tests\Fixtures\Post\Getters\PostsIndexGetter; use Binaryk\LaravelRestify\Tests\Fixtures\Post\Getters\PostsIndexInvokableGetter; use Binaryk\LaravelRestify\Tests\Fixtures\Post\Getters\PostsShowGetter; use Binaryk\LaravelRestify\Tests\Fixtures\Post\Getters\PostsShowInvokableGetter; +use Binaryk\LaravelRestify\Tests\Fixtures\Post\Post; use Binaryk\LaravelRestify\Tests\Fixtures\Post\PostRepository; use Binaryk\LaravelRestify\Tests\IntegrationTestCase; use Illuminate\Testing\Fluent\AssertableJson; @@ -62,4 +64,44 @@ public function test_could_perform_repository_invokable_getter(): void ->etc() ); } + + public function test_getter_can_access_filtered_query(): void + { + // Create 5 posts: 3 active, 2 inactive + Post::factory()->count(3)->create(['is_active' => true]); + Post::factory()->count(2)->create(['is_active' => false]); + + // Call getter without filter - should return all 5 posts + $this + ->getJson(PostRepository::getter(PostsFilteredQueryGetter::class)) + ->assertOk() + ->assertJson( + fn (AssertableJson $json) => $json + ->where('message', 'filtered query works') + ->where('count', 5) + ->etc() + ); + + // Call getter with active filter - should return only 3 active posts + $this + ->getJson(PostRepository::getter(PostsFilteredQueryGetter::class) . '?is_active=1') + ->assertOk() + ->assertJson( + fn (AssertableJson $json) => $json + ->where('message', 'filtered query works') + ->where('count', 3) + ->etc() + ); + + // Call getter with inactive filter - should return only 2 inactive posts + $this + ->getJson(PostRepository::getter(PostsFilteredQueryGetter::class) . '?is_active=0') + ->assertOk() + ->assertJson( + fn (AssertableJson $json) => $json + ->where('message', 'filtered query works') + ->where('count', 2) + ->etc() + ); + } } From 9380fa5ae97c74847e10238479790ca1eb70f702 Mon Sep 17 00:00:00 2001 From: binaryk Date: Mon, 17 Nov 2025 08:11:56 +0000 Subject: [PATCH 4/5] Fix styling --- src/Getters/Getter.php | 1 - tests/Getters/PerformGetterControllerTest.php | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/Getters/Getter.php b/src/Getters/Getter.php index 7abda010..72003fa6 100644 --- a/src/Getters/Getter.php +++ b/src/Getters/Getter.php @@ -6,7 +6,6 @@ use Binaryk\LaravelRestify\Http\Requests\RestifyRequest; use Binaryk\LaravelRestify\MCP\Actions\JsonSchemaFromRulesAction; use Binaryk\LaravelRestify\Restify; -use Binaryk\LaravelRestify\Services\Search\RepositorySearchService; use Binaryk\LaravelRestify\Traits\AuthorizedToRun; use Binaryk\LaravelRestify\Traits\AuthorizedToSee; use Binaryk\LaravelRestify\Traits\Make; diff --git a/tests/Getters/PerformGetterControllerTest.php b/tests/Getters/PerformGetterControllerTest.php index 21bf0c68..73fb5e7c 100644 --- a/tests/Getters/PerformGetterControllerTest.php +++ b/tests/Getters/PerformGetterControllerTest.php @@ -84,7 +84,7 @@ public function test_getter_can_access_filtered_query(): void // Call getter with active filter - should return only 3 active posts $this - ->getJson(PostRepository::getter(PostsFilteredQueryGetter::class) . '?is_active=1') + ->getJson(PostRepository::getter(PostsFilteredQueryGetter::class).'?is_active=1') ->assertOk() ->assertJson( fn (AssertableJson $json) => $json @@ -95,7 +95,7 @@ public function test_getter_can_access_filtered_query(): void // Call getter with inactive filter - should return only 2 inactive posts $this - ->getJson(PostRepository::getter(PostsFilteredQueryGetter::class) . '?is_active=0') + ->getJson(PostRepository::getter(PostsFilteredQueryGetter::class).'?is_active=0') ->assertOk() ->assertJson( fn (AssertableJson $json) => $json From 139eb5620bfd10473d372110f2f6ff8acc81c718 Mon Sep 17 00:00:00 2001 From: Eduard Lupacescu Date: Mon, 17 Nov 2025 10:13:55 +0200 Subject: [PATCH 5/5] fix: wip --- tests/Getters/ListGettersControllerTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/Getters/ListGettersControllerTest.php b/tests/Getters/ListGettersControllerTest.php index e51ae3b1..dd9729ab 100644 --- a/tests/Getters/ListGettersControllerTest.php +++ b/tests/Getters/ListGettersControllerTest.php @@ -16,7 +16,7 @@ public function test_could_list_getters_for_repository(): void fn (AssertableJson $json) => $json ->has('data') ->where('data.0.uriKey', 'posts-index-getter') - ->count('data', 4) + ->count('data', 5) ->etc() ); } @@ -31,7 +31,7 @@ public function test_could_list_getters_for_given_repository(): void fn (AssertableJson $json) => $json ->has('data') ->where('data.1.uriKey', 'posts-show-getter') - ->count('data', 5) + ->count('data', 6) ->etc() ); }