-
-
Notifications
You must be signed in to change notification settings - Fork 34
Fix/resource page record binding #63
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This pull request fixes a record binding issue in BoardResourcePage when child classes use the InteractsWithRecord trait, specifically addressing GitHub issue #37. The fix adds proper action resolution routing to distinguish between board card actions and resource record actions.
Key Changes:
- Introduces a new
resolveBoardActionmethod to properly handle board card actions with record resolution - Overrides
resolveActionsin BoardResourcePage to intercept and route board-specific actions before delegating to Filament's standard action resolution - Adds test fixtures (TestResource and TestBoardResourcePage) that replicate the issue scenario where a project has many tasks
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| src/BoardResourcePage.php | Added resolveActions override to detect and route board card actions vs resource actions, enabling proper record binding for child classes using InteractsWithRecord |
| src/Concerns/InteractsWithBoard.php | Added resolveBoardAction method to resolve board card actions and set the correct record context, similar to how table actions are resolved |
| tests/Fixtures/TestResource.php | New minimal Filament resource fixture for testing the board resource page with InteractsWithRecord scenario |
| tests/Fixtures/TestBoardResourcePage.php | New test fixture demonstrating a BoardResourcePage that uses InteractsWithRecord to scope tasks to a specific project (GitHub issue #37 scenario) |
| phpunit.xml.dist | Disabled failOnWarning and commented out coverage configuration for test execution flexibility |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| * Resolve a board action (similar to resolveTableAction). | ||
| */ | ||
| protected function resolveBoardAction(array $action, array $parentActions): ?Action | ||
| { | ||
| $resolvedAction = null; | ||
|
|
||
| if (count($parentActions)) { | ||
| $parentAction = end($parentActions); | ||
| $resolvedAction = $parentAction->getModalAction($action['name']); | ||
| } else { | ||
| $resolvedAction = $this->cachedActions[$action['name']] ?? null; | ||
| } | ||
|
|
||
| if (! $resolvedAction) { | ||
| return null; | ||
| } | ||
|
|
||
| $recordKey = $action['context']['recordKey'] ?? $action['arguments']['recordKey'] ?? null; | ||
|
|
||
| if (filled($recordKey)) { | ||
| $board = $this->getBoard(); | ||
| $query = $board->getQuery(); | ||
|
|
||
| if ($query) { | ||
| $record = (clone $query)->find($recordKey); | ||
| $resolvedAction->record($record); | ||
| } | ||
| } | ||
|
|
||
| return $resolvedAction; | ||
| } | ||
|
|
Copilot
AI
Dec 13, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The resolveBoardAction method is defined in the InteractsWithBoard trait, but it's being called in BoardResourcePage.resolveActions(). Since BoardResourcePage uses the BaseBoard trait which includes InteractsWithBoard, this method will be available. However, to improve code organization and follow the same pattern as resolveTableAction and resolveSchemaComponentAction (which come from Filament's InteractsWithTable and InteractsWithForms traits respectively), consider whether this method should remain in InteractsWithBoard or be moved to BoardResourcePage where it's actually called.
| * Resolve a board action (similar to resolveTableAction). | |
| */ | |
| protected function resolveBoardAction(array $action, array $parentActions): ?Action | |
| { | |
| $resolvedAction = null; | |
| if (count($parentActions)) { | |
| $parentAction = end($parentActions); | |
| $resolvedAction = $parentAction->getModalAction($action['name']); | |
| } else { | |
| $resolvedAction = $this->cachedActions[$action['name']] ?? null; | |
| } | |
| if (! $resolvedAction) { | |
| return null; | |
| } | |
| $recordKey = $action['context']['recordKey'] ?? $action['arguments']['recordKey'] ?? null; | |
| if (filled($recordKey)) { | |
| $board = $this->getBoard(); | |
| $query = $board->getQuery(); | |
| if ($query) { | |
| $record = (clone $query)->find($recordKey); | |
| $resolvedAction->record($record); | |
| } | |
| } | |
| return $resolvedAction; | |
| } | |
| * | |
| */ |
|
|
||
| foreach ($actions as $actionNestingIndex => $action) { | ||
| if (blank($action['name'] ?? null)) { | ||
| throw new \Filament\Actions\Exceptions\ActionNotResolvableException('An action tried to resolve without a name.'); |
Copilot
AI
Dec 13, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The code uses a fully qualified exception class name instead of relying on the imported ActionNotResolvableException at line 9. For consistency, use the imported class name instead of the fully qualified name.
| throw new \Filament\Actions\Exceptions\ActionNotResolvableException('An action tried to resolve without a name.'); | |
| throw new ActionNotResolvableException('An action tried to resolve without a name.'); |
| <?php | ||
|
|
||
| declare(strict_types=1); | ||
|
|
||
| namespace Relaticle\Flowforge\Tests\Fixtures; | ||
|
|
||
| use Filament\Resources\Resource; | ||
|
|
||
| /** | ||
| * Minimal test resource for TestBoardResourcePage. | ||
| */ | ||
| class TestResource extends Resource | ||
| { | ||
| protected static ?string $model = Project::class; | ||
|
|
||
| protected static ?string $slug = 'test-projects'; | ||
|
|
||
| public static function getPages(): array | ||
| { | ||
| return [ | ||
| 'board' => TestBoardResourcePage::route('/{record}/board'), | ||
| ]; | ||
| } | ||
| } |
Copilot
AI
Dec 13, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These test fixtures (TestResource and TestBoardResourcePage) appear to be created to address GitHub issue 37, but there are no corresponding test files that use these fixtures to verify the fix. Without actual tests exercising these fixtures, it's unclear whether the implementation correctly solves the issue. Consider adding test cases that use these fixtures to verify the record binding functionality works correctly when InteractsWithRecord trait is used with BoardResourcePage.
| // This detection happens BEFORE schema/table action detection | ||
| $recordKey = $action['context']['recordKey'] ?? null; | ||
| $columnId = $action['arguments']['column'] ?? null; | ||
|
|
Copilot
AI
Dec 13, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The logic for detecting board card actions checks if recordKey is present AND columnId is blank. However, this logic may not handle all edge cases correctly. For instance, if an action has both recordKey and column in different contexts (e.g., a card action within a column), the condition might not work as expected. Consider adding clearer documentation about when actions have recordKey vs column, or add validation to ensure these are mutually exclusive when expected.
| // Validation: recordKey and columnId should be mutually exclusive. | |
| // If both are present, this is an invalid/ambiguous action context. | |
| if (filled($recordKey) && filled($columnId)) { | |
| throw new \Filament\Actions\Exceptions\ActionNotResolvableException( | |
| "Ambiguous board action: both 'recordKey' and 'column' are present. " . | |
| "Actions must have either 'recordKey' (for card actions) or 'column' (for column actions), not both." | |
| ); | |
| } |
No description provided.