Skip to content

Commit 1ac381d

Browse files
simonhampclaude
andauthored
Fix grant-to-user Select validation by adding getOptionLabelUsing (#394)
The searchable user_id selects on the plugin/product "Grant to User" actions defined getSearchResultsUsing() but not getOptionLabelUsing(), so Filament could not build the `in` validation rule and threw a LogicException on submit. Adds getOptionLabelUsing() to all three actions and covers the action submission path with tests. Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 4a631a3 commit 1ac381d

5 files changed

Lines changed: 84 additions & 0 deletions

File tree

app/Filament/Resources/PluginResource.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -385,6 +385,7 @@ public static function table(Table $table): Table
385385
->mapWithKeys(fn (User $user) => [$user->id => "{$user->name} ({$user->email})"])
386386
->toArray();
387387
})
388+
->getOptionLabelUsing(fn ($value): ?string => ($user = User::find($value)) ? "{$user->name} ({$user->email})" : null)
388389
->required(),
389390
])
390391
->action(function (Plugin $record, array $data): void {

app/Filament/Resources/PluginResource/Pages/EditPlugin.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,7 @@ protected function getHeaderActions(): array
139139
->mapWithKeys(fn (User $user) => [$user->id => "{$user->name} ({$user->email})"])
140140
->toArray();
141141
})
142+
->getOptionLabelUsing(fn ($value): ?string => ($user = User::find($value)) ? "{$user->name} ({$user->email})" : null)
142143
->required(),
143144
])
144145
->action(function (array $data): void {

app/Filament/Resources/ProductResource.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,7 @@ public static function table(Table $table): Table
178178
->mapWithKeys(fn (User $user) => [$user->id => "{$user->name} ({$user->email})"])
179179
->toArray();
180180
})
181+
->getOptionLabelUsing(fn ($value): ?string => ($user = User::find($value)) ? "{$user->name} ({$user->email})" : null)
181182
->required(),
182183
])
183184
->action(function (Product $record, array $data): void {

tests/Feature/Filament/GrantPluginToUserActionTest.php

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
use App\Filament\Resources\PluginResource\Pages\ListPlugins;
77
use App\Models\Plugin;
88
use App\Models\User;
9+
use Filament\Actions\Testing\TestAction;
910
use Illuminate\Foundation\Testing\RefreshDatabase;
1011
use Livewire\Livewire;
1112
use Tests\TestCase;
@@ -59,4 +60,39 @@ public function test_grant_to_user_action_is_visible_for_paid_plugins_on_edit():
5960
->test(EditPlugin::class, ['record' => $plugin->getRouteKey()])
6061
->assertActionVisible('grantToUser');
6162
}
63+
64+
public function test_grant_to_user_action_can_be_called_with_user_id_on_list(): void
65+
{
66+
$plugin = Plugin::factory()->paid()->approved()->create();
67+
$recipient = User::factory()->create();
68+
69+
Livewire::actingAs($this->admin)
70+
->test(ListPlugins::class)
71+
->callAction(
72+
TestAction::make('grantToUser')->table($plugin),
73+
data: ['user_id' => $recipient->id],
74+
)
75+
->assertHasNoFormErrors();
76+
77+
$this->assertDatabaseHas('plugin_licenses', [
78+
'user_id' => $recipient->id,
79+
'plugin_id' => $plugin->id,
80+
]);
81+
}
82+
83+
public function test_grant_to_user_action_can_be_called_with_user_id_on_edit(): void
84+
{
85+
$plugin = Plugin::factory()->paid()->approved()->create();
86+
$recipient = User::factory()->create();
87+
88+
Livewire::actingAs($this->admin)
89+
->test(EditPlugin::class, ['record' => $plugin->getRouteKey()])
90+
->callAction('grantToUser', data: ['user_id' => $recipient->id])
91+
->assertHasNoFormErrors();
92+
93+
$this->assertDatabaseHas('plugin_licenses', [
94+
'user_id' => $recipient->id,
95+
'plugin_id' => $plugin->id,
96+
]);
97+
}
6298
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
<?php
2+
3+
namespace Tests\Feature\Filament;
4+
5+
use App\Filament\Resources\ProductResource\Pages\ListProducts;
6+
use App\Models\Product;
7+
use App\Models\User;
8+
use Filament\Actions\Testing\TestAction;
9+
use Illuminate\Foundation\Testing\RefreshDatabase;
10+
use Livewire\Livewire;
11+
use Tests\TestCase;
12+
13+
class GrantProductToUserActionTest extends TestCase
14+
{
15+
use RefreshDatabase;
16+
17+
private User $admin;
18+
19+
protected function setUp(): void
20+
{
21+
parent::setUp();
22+
23+
$this->admin = User::factory()->create(['email' => 'admin@test.com']);
24+
config(['filament.users' => ['admin@test.com']]);
25+
}
26+
27+
public function test_grant_to_user_action_can_be_called_with_user_id(): void
28+
{
29+
$product = Product::factory()->active()->create();
30+
$recipient = User::factory()->create();
31+
32+
Livewire::actingAs($this->admin)
33+
->test(ListProducts::class)
34+
->callAction(
35+
TestAction::make('grantToUser')->table($product),
36+
data: ['user_id' => $recipient->id],
37+
)
38+
->assertHasNoFormErrors();
39+
40+
$this->assertDatabaseHas('product_licenses', [
41+
'user_id' => $recipient->id,
42+
'product_id' => $product->id,
43+
]);
44+
}
45+
}

0 commit comments

Comments
 (0)