Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 33 additions & 2 deletions docs-v3/content/docs/api/getters.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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:
Expand Down
6 changes: 6 additions & 0 deletions src/Http/Requests/Concerns/InteractWithRepositories.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -121,4 +122,9 @@ public function isViaRepository(): bool

return $parentRepository && $parentRepositoryId;
}

public function filteredQuery(): Builder
{
return RepositorySearchService::make()->search($this, $this->repository());
}
}
20 changes: 10 additions & 10 deletions src/MCP/Bootstrap/BootMcpTools.php
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}

Expand Down Expand Up @@ -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);

Expand All @@ -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);

Expand Down
24 changes: 24 additions & 0 deletions tests/Fixtures/Post/Getters/PostsFilteredQueryGetter.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?php

namespace Binaryk\LaravelRestify\Tests\Fixtures\Post\Getters;

use Binaryk\LaravelRestify\Getters\Getter;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;

class PostsFilteredQueryGetter extends Getter
{
public static $uriKey = 'posts-filtered-query-getter';

public function handle(Request $request): JsonResponse
{
$query = $request->filteredQuery();

$count = $query->count();

return response()->json([
'message' => 'filtered query works',
'count' => $count,
]);
}
}
2 changes: 2 additions & 0 deletions tests/Fixtures/Post/PostRepository.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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,
];
Expand Down
4 changes: 2 additions & 2 deletions tests/Getters/ListGettersControllerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -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()
);
}
Expand All @@ -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()
);
}
Expand Down
42 changes: 42 additions & 0 deletions tests/Getters/PerformGetterControllerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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()
);
}
}
Loading