Skip to content

Commit 16e5c40

Browse files
Paginated data in the resource data tool (#7)
* #6: Paginated data in the resource data tool Updated .gitignore as well * Fix styling * Removed redundant argument * Fix styling
1 parent fe83766 commit 16e5c40

File tree

5 files changed

+97
-48
lines changed

5 files changed

+97
-48
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: 81 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@
33
namespace Kirschbaum\Loop\Filament;
44

55
use Exception;
6+
use Filament\Resources\Pages\ListRecords;
7+
use Filament\Resources\Pages\PageRegistration;
8+
use Filament\Resources\Resource;
69
use Filament\Tables\Columns\Column;
710
use Illuminate\Database\Eloquent\Model;
811
use Illuminate\Support\Facades\Log;
@@ -11,7 +14,9 @@
1114
use Kirschbaum\Loop\Contracts\Tool;
1215
use Kirschbaum\Loop\Exceptions\LoopMcpException;
1316
use Kirschbaum\Loop\Filament\Concerns\ProvidesFilamentResourceInstance;
17+
use Kirschbaum\Loop\Filament\Exceptions\FilamentResourceIndexPageDoesNotExist;
1418
use Prism\Prism\Tool as PrismTool;
19+
use Throwable;
1520

1621
class GetFilamentResourceDataTool implements Tool
1722
{
@@ -23,19 +28,17 @@ public function build(): PrismTool
2328
return app(PrismTool::class)
2429
->as($this->getName())
2530
->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.')
26-
->withStringParameter('resource', 'The resource class name of the resource to get data for, from the list_filament_resources tool.', required: true)
31+
->withStringParameter('resource', 'The resource class name of the resource to get data for, from the list_filament_resources tool.')
2732
->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) {
33+
->withNumberParameter('perPage', 'The resource data is paginated. This is the number of records per page. It defaults to 10', required: false)
34+
->withNumberParameter('page', 'The resource data is paginated. This is the page the paginated results should be from.', required: false)
35+
->using(function (string $resource, ?string $filters = null, ?int $perPage = 10, ?int $page = null) {
2936
$resource = $this->getResourceInstance($resource);
3037
$filters = $this->parseFilters($filters);
3138

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

36-
/** @var InteractsWithTable $listPage */
37-
$listPage = new $component;
38-
$listPage->bootedInteractsWithTable();
3942
$table = $listPage->getTable();
4043
$tableColumns = $table->getColumns();
4144

@@ -69,42 +72,50 @@ public function build(): PrismTool
6972
}
7073
}
7174

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}'.");
75+
$results = $listPage->getFilteredTableQuery()->paginate(perPage: $perPage, page: $page);
76+
77+
$outputData = [
78+
'data' => $results->getCollection()
79+
->map(function (Model $model) use ($tableColumns) {
80+
$rowData = [
81+
$model->getKeyName() => $model->getKey(),
82+
];
83+
84+
foreach ($tableColumns as $column) {
85+
/** @var Column $column */
86+
$columnName = $column->getName();
87+
88+
try {
89+
if (str_contains($columnName, '.')) {
90+
$relationName = strtok($columnName, '.');
91+
92+
if (method_exists($model, $relationName)) {
93+
$model->loadMissing($relationName);
94+
$value = data_get($model, $columnName);
95+
} else {
96+
$value = null;
97+
Log::warning("Relation '{$relationName}' not found on model for column '{$columnName}'.");
98+
}
99+
} else {
100+
$value = $model->getAttribute($columnName);
101+
}
102+
103+
$rowData[$columnName] = $value;
104+
} catch (Exception $e) {
105+
$rowData[$columnName] = null;
106+
Log::error("Could not retrieve value for column '{$columnName}' on model ID {$model->getKey()}': {$e->getMessage()}");
94107
}
95-
} else {
96-
$value = $model->getAttribute($columnName);
97108
}
98109

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-
}
110+
return $rowData;
111+
}),
105112

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

109120
return json_encode($outputData);
110121
} catch (Exception $e) {
@@ -144,4 +155,35 @@ protected function parseFilters(?string $filtersJson = null): array
144155

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

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)