From b761b395c1b1e3c0792a10ffb8523dc99cb02f0f Mon Sep 17 00:00:00 2001 From: Brandon Ferens Date: Tue, 10 Jun 2025 07:33:10 -0700 Subject: [PATCH 1/4] #6: Paginated data in the resource data tool Updated .gitignore as well --- .gitignore | 12 +- .../FilamentResourceIndexPageDoesNotExist.php | 7 + src/FilamentToolkit.php | 4 +- src/GetFilamentResourceDataTool.php | 120 ++++++++++++------ src/ListFilamentResourcesTool.php | 2 +- 5 files changed, 98 insertions(+), 47 deletions(-) create mode 100644 src/Exceptions/FilamentResourceIndexPageDoesNotExist.php diff --git a/.gitignore b/.gitignore index 33d7849..a229f4f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,7 @@ -vendor -composer.lock -node_modules -build +/.idea +/build +/node_modules +/vendor +.DS_Store .pint.cache -.idea -build \ No newline at end of file +composer.lock diff --git a/src/Exceptions/FilamentResourceIndexPageDoesNotExist.php b/src/Exceptions/FilamentResourceIndexPageDoesNotExist.php new file mode 100644 index 0000000..76cd519 --- /dev/null +++ b/src/Exceptions/FilamentResourceIndexPageDoesNotExist.php @@ -0,0 +1,7 @@ +[] $resources */ public function __construct( public readonly array $resources = [], diff --git a/src/GetFilamentResourceDataTool.php b/src/GetFilamentResourceDataTool.php index f0a2efa..341f745 100644 --- a/src/GetFilamentResourceDataTool.php +++ b/src/GetFilamentResourceDataTool.php @@ -3,7 +3,12 @@ namespace Kirschbaum\Loop\Filament; use Exception; +use Filament\Pages\Page; +use Filament\Resources\Pages\ListRecords; +use Filament\Resources\Pages\PageRegistration; +use Filament\Resources\Resource; use Filament\Tables\Columns\Column; +use Filament\Tables\TableComponent; use Illuminate\Database\Eloquent\Model; use Illuminate\Support\Facades\Log; use JsonException; @@ -11,7 +16,9 @@ use Kirschbaum\Loop\Contracts\Tool; use Kirschbaum\Loop\Exceptions\LoopMcpException; use Kirschbaum\Loop\Filament\Concerns\ProvidesFilamentResourceInstance; +use Kirschbaum\Loop\Filament\Exceptions\FilamentResourceIndexPageDoesNotExist; use Prism\Prism\Tool as PrismTool; +use Throwable; class GetFilamentResourceDataTool implements Tool { @@ -25,17 +32,15 @@ public function build(): PrismTool ->for('Gets the data for a given Filament resource, applying optional filters (try to use them). Always call the describe_filament_resource tool before calling this tool. Always try to use the available filters to get the data you need.') ->withStringParameter('resource', 'The resource class name of the resource to get data for, from the list_filament_resources tool.', required: true) ->withStringParameter('filters', 'JSON string of filters to apply (e.g., \'{"status": "published", "author_id": [1, 2]}\').', required: false) - ->using(function (string $resource, ?string $filters = null) { + ->withNumberParameter('perPage', 'The resource data is paginated. This is the number of records per page. It defaults to 10', required: false) + ->withNumberParameter('page', 'The resource data is paginated. This is the page the paginated results should be from.', required: false) + ->using(function (string $resource, ?string $filters = null, ?int $perPage = 10, ?int $page = null) { $resource = $this->getResourceInstance($resource); $filters = $this->parseFilters($filters); try { - $listPageClass = $resource::getPages()['index']; - $component = $listPageClass->getPage(); + $listPage = $this->getListPage($resource); - /** @var InteractsWithTable $listPage */ - $listPage = new $component; - $listPage->bootedInteractsWithTable(); $table = $listPage->getTable(); $tableColumns = $table->getColumns(); @@ -69,42 +74,50 @@ public function build(): PrismTool } } - // TODO: Allow the tool to specify the number of results to return with a max - $results = $listPage->getFilteredTableQuery()->take(10)->get(); - - $outputData = $results->map(function (Model $model) use ($tableColumns) { - $rowData = [ - $model->getKeyName() => $model->getKey(), - ]; - - foreach ($tableColumns as $column) { - /** @var Column $column */ - $columnName = $column->getName(); - - try { - if (str_contains($columnName, '.')) { - $relationName = strtok($columnName, '.'); - - if (method_exists($model, $relationName)) { - $model->loadMissing($relationName); - $value = data_get($model, $columnName); - } else { - $value = null; - Log::warning("Relation '{$relationName}' not found on model for column '{$columnName}'."); + $results = $listPage->getFilteredTableQuery()->paginate(perPage: $perPage, page: $page); + + $outputData = [ + 'data' => $results->getCollection() + ->map(function (Model $model) use ($tableColumns) { + $rowData = [ + $model->getKeyName() => $model->getKey(), + ]; + + foreach ($tableColumns as $column) { + /** @var Column $column */ + $columnName = $column->getName(); + + try { + if (str_contains($columnName, '.')) { + $relationName = strtok($columnName, '.'); + + if (method_exists($model, $relationName)) { + $model->loadMissing($relationName); + $value = data_get($model, $columnName); + } else { + $value = null; + Log::warning("Relation '{$relationName}' not found on model for column '{$columnName}'."); + } + } else { + $value = $model->getAttribute($columnName); + } + + $rowData[$columnName] = $value; + } catch (Exception $e) { + $rowData[$columnName] = null; + Log::error("Could not retrieve value for column '{$columnName}' on model ID {$model->getKey()}': {$e->getMessage()}"); } - } else { - $value = $model->getAttribute($columnName); } - $rowData[$columnName] = $value; - } catch (Exception $e) { - $rowData[$columnName] = null; - Log::error("Could not retrieve value for column '{$columnName}' on model ID {$model->getKey()}': {$e->getMessage()}"); - } - } + return $rowData; + }), - return $rowData; - }); + 'pagination' => [ + 'total' => $results->total(), + 'per_page' => $results->perPage(), + 'current_page' => $results->currentPage(), + ], + ]; return json_encode($outputData); } catch (Exception $e) { @@ -144,4 +157,35 @@ protected function parseFilters(?string $filtersJson = null): array return $filters; } + + /** + * @throws Throwable + */ + protected function getListPage(Resource $resource): ListRecords + { + /** + * @var ?PageRegistration $listPageClass + */ + $listPageClass = data_get($resource::getPages(), 'index'); + + throw_unless( + $listPageClass instanceof PageRegistration, + FilamentResourceIndexPageDoesNotExist::class, + 'No index page exists for ['.get_class($resource).']' + ); + + /** + * @var class-string $component + */ + $component = $listPageClass->getPage(); + + /** + * @var ListRecords $listPage + */ + $listPage = new $component; + + $listPage->bootedInteractsWithTable(); + + return $listPage; + } } diff --git a/src/ListFilamentResourcesTool.php b/src/ListFilamentResourcesTool.php index 7e875e8..09f34a9 100644 --- a/src/ListFilamentResourcesTool.php +++ b/src/ListFilamentResourcesTool.php @@ -19,7 +19,7 @@ class ListFilamentResourcesTool implements Tool /** * @param resource[] $resources */ - public function __construct(private array $resources = []) {} + public function __construct(protected readonly array $resources = []) {} public function build(): PrismTool { From c25c609f202bf2e40eee82f535f09997756a21b6 Mon Sep 17 00:00:00 2001 From: brandonferens <1819546+brandonferens@users.noreply.github.com> Date: Tue, 10 Jun 2025 14:33:46 +0000 Subject: [PATCH 2/4] Fix styling --- src/GetFilamentResourceDataTool.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/GetFilamentResourceDataTool.php b/src/GetFilamentResourceDataTool.php index 341f745..6a10081 100644 --- a/src/GetFilamentResourceDataTool.php +++ b/src/GetFilamentResourceDataTool.php @@ -3,12 +3,10 @@ namespace Kirschbaum\Loop\Filament; use Exception; -use Filament\Pages\Page; use Filament\Resources\Pages\ListRecords; use Filament\Resources\Pages\PageRegistration; use Filament\Resources\Resource; use Filament\Tables\Columns\Column; -use Filament\Tables\TableComponent; use Illuminate\Database\Eloquent\Model; use Illuminate\Support\Facades\Log; use JsonException; From 5891152d5f86d1f6b2fe95bd9eb9eb66a4b4977a Mon Sep 17 00:00:00 2001 From: Brandon Ferens Date: Tue, 10 Jun 2025 08:00:15 -0700 Subject: [PATCH 3/4] Removed redundant argument --- src/GetFilamentResourceDataTool.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/GetFilamentResourceDataTool.php b/src/GetFilamentResourceDataTool.php index 6a10081..4d4e715 100644 --- a/src/GetFilamentResourceDataTool.php +++ b/src/GetFilamentResourceDataTool.php @@ -3,10 +3,12 @@ namespace Kirschbaum\Loop\Filament; use Exception; +use Filament\Pages\Page; use Filament\Resources\Pages\ListRecords; use Filament\Resources\Pages\PageRegistration; use Filament\Resources\Resource; use Filament\Tables\Columns\Column; +use Filament\Tables\TableComponent; use Illuminate\Database\Eloquent\Model; use Illuminate\Support\Facades\Log; use JsonException; @@ -28,7 +30,7 @@ public function build(): PrismTool return app(PrismTool::class) ->as($this->getName()) ->for('Gets the data for a given Filament resource, applying optional filters (try to use them). Always call the describe_filament_resource tool before calling this tool. Always try to use the available filters to get the data you need.') - ->withStringParameter('resource', 'The resource class name of the resource to get data for, from the list_filament_resources tool.', required: true) + ->withStringParameter('resource', 'The resource class name of the resource to get data for, from the list_filament_resources tool.') ->withStringParameter('filters', 'JSON string of filters to apply (e.g., \'{"status": "published", "author_id": [1, 2]}\').', required: false) ->withNumberParameter('perPage', 'The resource data is paginated. This is the number of records per page. It defaults to 10', required: false) ->withNumberParameter('page', 'The resource data is paginated. This is the page the paginated results should be from.', required: false) From 496ac3c580da78121a991c17ec75f5971faf4e11 Mon Sep 17 00:00:00 2001 From: brandonferens <1819546+brandonferens@users.noreply.github.com> Date: Tue, 10 Jun 2025 15:00:34 +0000 Subject: [PATCH 4/4] Fix styling --- src/GetFilamentResourceDataTool.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/GetFilamentResourceDataTool.php b/src/GetFilamentResourceDataTool.php index 4d4e715..9a18267 100644 --- a/src/GetFilamentResourceDataTool.php +++ b/src/GetFilamentResourceDataTool.php @@ -3,12 +3,10 @@ namespace Kirschbaum\Loop\Filament; use Exception; -use Filament\Pages\Page; use Filament\Resources\Pages\ListRecords; use Filament\Resources\Pages\PageRegistration; use Filament\Resources\Resource; use Filament\Tables\Columns\Column; -use Filament\Tables\TableComponent; use Illuminate\Database\Eloquent\Model; use Illuminate\Support\Facades\Log; use JsonException;