Skip to content

Commit b761b39

Browse files
committed
#6: Paginated data in the resource data tool
Updated .gitignore as well
1 parent fe83766 commit b761b39

File tree

5 files changed

+98
-47
lines changed

5 files changed

+98
-47
lines changed

.gitignore

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
vendor
2-
composer.lock
3-
node_modules
4-
build
1+
/.idea
2+
/build
3+
/node_modules
4+
/vendor
5+
.DS_Store
56
.pint.cache
6-
.idea
7-
build
7+
composer.lock
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<?php
2+
3+
namespace Kirschbaum\Loop\Filament\Exceptions;
4+
5+
use Exception;
6+
7+
class FilamentResourceIndexPageDoesNotExist extends Exception {}

src/FilamentToolkit.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,14 @@
99
use Kirschbaum\Loop\Enums\Mode;
1010

1111
/**
12-
* @method static self make(Resource[] $resources, Mode $mode = Mode::ReadOnly)
12+
* @method static self make(string[] $resources = [], Mode $mode = Mode::ReadOnly)
1313
*/
1414
class FilamentToolkit implements Toolkit
1515
{
1616
use Makeable;
1717

1818
/**
19-
* @param resource[] $resources
19+
* @param class-string<resource>[] $resources
2020
*/
2121
public function __construct(
2222
public readonly array $resources = [],

src/GetFilamentResourceDataTool.php

Lines changed: 82 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,22 @@
33
namespace Kirschbaum\Loop\Filament;
44

55
use Exception;
6+
use Filament\Pages\Page;
7+
use Filament\Resources\Pages\ListRecords;
8+
use Filament\Resources\Pages\PageRegistration;
9+
use Filament\Resources\Resource;
610
use Filament\Tables\Columns\Column;
11+
use Filament\Tables\TableComponent;
712
use Illuminate\Database\Eloquent\Model;
813
use Illuminate\Support\Facades\Log;
914
use JsonException;
1015
use Kirschbaum\Loop\Concerns\Makeable;
1116
use Kirschbaum\Loop\Contracts\Tool;
1217
use Kirschbaum\Loop\Exceptions\LoopMcpException;
1318
use Kirschbaum\Loop\Filament\Concerns\ProvidesFilamentResourceInstance;
19+
use Kirschbaum\Loop\Filament\Exceptions\FilamentResourceIndexPageDoesNotExist;
1420
use Prism\Prism\Tool as PrismTool;
21+
use Throwable;
1522

1623
class GetFilamentResourceDataTool implements Tool
1724
{
@@ -25,17 +32,15 @@ public function build(): PrismTool
2532
->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.')
2633
->withStringParameter('resource', 'The resource class name of the resource to get data for, from the list_filament_resources tool.', required: true)
2734
->withStringParameter('filters', 'JSON string of filters to apply (e.g., \'{"status": "published", "author_id": [1, 2]}\').', required: false)
28-
->using(function (string $resource, ?string $filters = null) {
35+
->withNumberParameter('perPage', 'The resource data is paginated. This is the number of records per page. It defaults to 10', required: false)
36+
->withNumberParameter('page', 'The resource data is paginated. This is the page the paginated results should be from.', required: false)
37+
->using(function (string $resource, ?string $filters = null, ?int $perPage = 10, ?int $page = null) {
2938
$resource = $this->getResourceInstance($resource);
3039
$filters = $this->parseFilters($filters);
3140

3241
try {
33-
$listPageClass = $resource::getPages()['index'];
34-
$component = $listPageClass->getPage();
42+
$listPage = $this->getListPage($resource);
3543

36-
/** @var InteractsWithTable $listPage */
37-
$listPage = new $component;
38-
$listPage->bootedInteractsWithTable();
3944
$table = $listPage->getTable();
4045
$tableColumns = $table->getColumns();
4146

@@ -69,42 +74,50 @@ public function build(): PrismTool
6974
}
7075
}
7176

72-
// TODO: Allow the tool to specify the number of results to return with a max
73-
$results = $listPage->getFilteredTableQuery()->take(10)->get();
74-
75-
$outputData = $results->map(function (Model $model) use ($tableColumns) {
76-
$rowData = [
77-
$model->getKeyName() => $model->getKey(),
78-
];
79-
80-
foreach ($tableColumns as $column) {
81-
/** @var Column $column */
82-
$columnName = $column->getName();
83-
84-
try {
85-
if (str_contains($columnName, '.')) {
86-
$relationName = strtok($columnName, '.');
87-
88-
if (method_exists($model, $relationName)) {
89-
$model->loadMissing($relationName);
90-
$value = data_get($model, $columnName);
91-
} else {
92-
$value = null;
93-
Log::warning("Relation '{$relationName}' not found on model for column '{$columnName}'.");
77+
$results = $listPage->getFilteredTableQuery()->paginate(perPage: $perPage, page: $page);
78+
79+
$outputData = [
80+
'data' => $results->getCollection()
81+
->map(function (Model $model) use ($tableColumns) {
82+
$rowData = [
83+
$model->getKeyName() => $model->getKey(),
84+
];
85+
86+
foreach ($tableColumns as $column) {
87+
/** @var Column $column */
88+
$columnName = $column->getName();
89+
90+
try {
91+
if (str_contains($columnName, '.')) {
92+
$relationName = strtok($columnName, '.');
93+
94+
if (method_exists($model, $relationName)) {
95+
$model->loadMissing($relationName);
96+
$value = data_get($model, $columnName);
97+
} else {
98+
$value = null;
99+
Log::warning("Relation '{$relationName}' not found on model for column '{$columnName}'.");
100+
}
101+
} else {
102+
$value = $model->getAttribute($columnName);
103+
}
104+
105+
$rowData[$columnName] = $value;
106+
} catch (Exception $e) {
107+
$rowData[$columnName] = null;
108+
Log::error("Could not retrieve value for column '{$columnName}' on model ID {$model->getKey()}': {$e->getMessage()}");
94109
}
95-
} else {
96-
$value = $model->getAttribute($columnName);
97110
}
98111

99-
$rowData[$columnName] = $value;
100-
} catch (Exception $e) {
101-
$rowData[$columnName] = null;
102-
Log::error("Could not retrieve value for column '{$columnName}' on model ID {$model->getKey()}': {$e->getMessage()}");
103-
}
104-
}
112+
return $rowData;
113+
}),
105114

106-
return $rowData;
107-
});
115+
'pagination' => [
116+
'total' => $results->total(),
117+
'per_page' => $results->perPage(),
118+
'current_page' => $results->currentPage(),
119+
],
120+
];
108121

109122
return json_encode($outputData);
110123
} catch (Exception $e) {
@@ -144,4 +157,35 @@ protected function parseFilters(?string $filtersJson = null): array
144157

145158
return $filters;
146159
}
160+
161+
/**
162+
* @throws Throwable
163+
*/
164+
protected function getListPage(Resource $resource): ListRecords
165+
{
166+
/**
167+
* @var ?PageRegistration $listPageClass
168+
*/
169+
$listPageClass = data_get($resource::getPages(), 'index');
170+
171+
throw_unless(
172+
$listPageClass instanceof PageRegistration,
173+
FilamentResourceIndexPageDoesNotExist::class,
174+
'No index page exists for ['.get_class($resource).']'
175+
);
176+
177+
/**
178+
* @var class-string<ListRecords> $component
179+
*/
180+
$component = $listPageClass->getPage();
181+
182+
/**
183+
* @var ListRecords $listPage
184+
*/
185+
$listPage = new $component;
186+
187+
$listPage->bootedInteractsWithTable();
188+
189+
return $listPage;
190+
}
147191
}

src/ListFilamentResourcesTool.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ class ListFilamentResourcesTool implements Tool
1919
/**
2020
* @param resource[] $resources
2121
*/
22-
public function __construct(private array $resources = []) {}
22+
public function __construct(protected readonly array $resources = []) {}
2323

2424
public function build(): PrismTool
2525
{

0 commit comments

Comments
 (0)