Skip to content

Commit 09ac3c5

Browse files
fix: make:filament-user crashes without default panel (#20026)
* fix: --panel ignored in make:filament-user, crashes without default panel The command's configurePanel() already resolves the panel from --panel or interactive prompt and stores it in $this->panel, but line 84 called Filament::getCurrentOrDefaultPanel() instead — a method that fails in CLI context when no panel has ->default() set. Replaced with $this->panel which is already correctly resolved by the HasPanel trait. Closes #20025 * fix: make:filament-user prompt crashes without default panel The select() prompt in configurePanel() calls Filament::getDefaultPanel()->getId() as its default option. When no panel has ->default() set, this throws NoDefaultPanelSetException before the prompt can even render. Wrapped in try/catch so the prompt still shows (without pre-selection) when no default panel exists. * Create MakeUserCommandTest.php --------- Co-authored-by: Dan Harrin <git@danharrin.com>
1 parent fac8bae commit 09ac3c5

3 files changed

Lines changed: 160 additions & 11 deletions

File tree

packages/panels/src/Commands/MakeUserCommand.php

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
namespace Filament\Commands;
44

5-
use Filament\Facades\Filament;
65
use Filament\Support\Commands\Concerns\HasPanel;
76
use Illuminate\Auth\EloquentUserProvider;
87
use Illuminate\Console\Command;
@@ -81,7 +80,7 @@ public function handle(): int
8180

8281
$this->options = $this->options();
8382

84-
if (! Filament::getCurrentOrDefaultPanel()) {
83+
if (! $this->panel) {
8584
$this->error('Filament has not been installed yet: php artisan filament:install --panels');
8685

8786
return static::FAILURE;

packages/support/src/Commands/Concerns/HasPanel.php

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
namespace Filament\Support\Commands\Concerns;
44

5+
use Filament\Exceptions\NoDefaultPanelSetException;
56
use Filament\Facades\Filament;
67
use Filament\Panel;
78
use Illuminate\Support\Arr;
@@ -36,15 +37,26 @@ protected function configurePanel(string $question, ?string $initialQuestion = n
3637

3738
$panels = Filament::getPanels();
3839

39-
/** @var Panel $panel */
40-
$panel = (count($panels) > 1) ? $panels[select(
41-
label: $question,
42-
options: array_map(
43-
fn (Panel $panel): string => $panel->getId(),
44-
$panels,
45-
),
46-
default: Filament::getDefaultPanel()->getId(),
47-
)] : Arr::first($panels);
40+
if (count($panels) > 1) {
41+
try {
42+
$defaultPanelId = Filament::getDefaultPanel()->getId();
43+
} catch (NoDefaultPanelSetException) {
44+
$defaultPanelId = null;
45+
}
46+
47+
/** @var Panel $panel */
48+
$panel = $panels[select(
49+
label: $question,
50+
options: array_map(
51+
fn (Panel $panel): string => $panel->getId(),
52+
$panels,
53+
),
54+
default: $defaultPanelId,
55+
)];
56+
} else {
57+
/** @var Panel $panel */
58+
$panel = Arr::first($panels);
59+
}
4860

4961
$this->panel = $panel;
5062
}
Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
<?php
2+
3+
use Filament\Exceptions\NoDefaultPanelSetException;
4+
use Filament\Facades\Filament;
5+
use Filament\PanelRegistry;
6+
use Filament\Tests\Fixtures\Models\User;
7+
use Filament\Tests\TestCase;
8+
use Illuminate\Support\Facades\Hash;
9+
10+
uses(TestCase::class)->group('commands');
11+
12+
$removeDefaultPanel = function (): void {
13+
$registry = app(PanelRegistry::class);
14+
15+
foreach ($registry->all() as $panel) {
16+
invade($panel)->isDefault = false;
17+
}
18+
19+
$registry->defaultPanel = null;
20+
21+
expect(fn (): mixed => Filament::getDefaultPanel())
22+
->toThrow(NoDefaultPanelSetException::class);
23+
};
24+
25+
it('can create a user with all the details passed as options', function (): void {
26+
$this->artisan('make:filament-user', [
27+
'--name' => 'Dan Harrin',
28+
'--email' => 'dan@filamentphp.com',
29+
'--password' => 'password',
30+
'--panel' => 'admin',
31+
'--no-interaction' => true,
32+
])->assertSuccessful();
33+
34+
$user = User::query()->where('email', 'dan@filamentphp.com')->first();
35+
36+
expect($user)->not->toBeNull()
37+
->and($user->name)->toBe('Dan Harrin');
38+
});
39+
40+
it('hashes the user password', function (): void {
41+
$this->artisan('make:filament-user', [
42+
'--name' => 'Dan Harrin',
43+
'--email' => 'dan@filamentphp.com',
44+
'--password' => 'password',
45+
'--panel' => 'admin',
46+
'--no-interaction' => true,
47+
])->assertSuccessful();
48+
49+
$user = User::query()->where('email', 'dan@filamentphp.com')->first();
50+
51+
expect($user->password)->not->toBe('password')
52+
->and(Hash::check('password', $user->password))->toBeTrue();
53+
});
54+
55+
it('prompts for the user details when they are not passed as options', function (): void {
56+
$this->artisan('make:filament-user', [
57+
'--panel' => 'admin',
58+
])
59+
->expectsQuestion('Name', 'Dan Harrin')
60+
->expectsQuestion('Email address', 'dan@filamentphp.com')
61+
->expectsQuestion('Password', 'password')
62+
->assertSuccessful();
63+
64+
$user = User::query()->where('email', 'dan@filamentphp.com')->first();
65+
66+
expect($user)->not->toBeNull()
67+
->and($user->name)->toBe('Dan Harrin')
68+
->and(Hash::check('password', $user->password))->toBeTrue();
69+
});
70+
71+
it('prompts to select the panel when `--panel` is not passed', function (): void {
72+
$this->artisan('make:filament-user', [
73+
'--name' => 'Dan Harrin',
74+
'--email' => 'dan@filamentphp.com',
75+
'--password' => 'password',
76+
])
77+
->expectsQuestion('Which panel would you like to create this user in?', 'admin')
78+
->assertSuccessful();
79+
80+
expect(User::query()->where('email', 'dan@filamentphp.com')->exists())
81+
->toBeTrue();
82+
});
83+
84+
it('includes the panel login URL in the success message', function (): void {
85+
$loginUrl = Filament::getPanel('admin')->getLoginUrl();
86+
87+
$this->artisan('make:filament-user', [
88+
'--name' => 'Dan Harrin',
89+
'--email' => 'dan@filamentphp.com',
90+
'--password' => 'password',
91+
'--panel' => 'admin',
92+
'--no-interaction' => true,
93+
])
94+
->expectsOutputToContain($loginUrl)
95+
->assertSuccessful();
96+
});
97+
98+
it('fails when Filament has not been installed', function (): void {
99+
app(PanelRegistry::class)->panels = [];
100+
101+
$this->artisan('make:filament-user', [
102+
'--no-interaction' => true,
103+
])
104+
->expectsOutputToContain('Filament has not been installed yet')
105+
->assertFailed();
106+
107+
expect(User::query()->count())->toBe(0);
108+
});
109+
110+
it('can create a user in the panel given by `--panel` when no default panel is set', function () use ($removeDefaultPanel): void {
111+
$removeDefaultPanel();
112+
113+
$this->artisan('make:filament-user', [
114+
'--panel' => 'admin',
115+
'--name' => 'Dan Harrin',
116+
'--email' => 'dan@filamentphp.com',
117+
'--password' => 'password',
118+
'--no-interaction' => true,
119+
])->assertSuccessful();
120+
121+
expect(User::query()->where('email', 'dan@filamentphp.com')->exists())
122+
->toBeTrue();
123+
});
124+
125+
it('can prompt for the panel when no default panel is set', function () use ($removeDefaultPanel): void {
126+
$removeDefaultPanel();
127+
128+
$this->artisan('make:filament-user', [
129+
'--name' => 'Dan Harrin',
130+
'--email' => 'dan@filamentphp.com',
131+
'--password' => 'password',
132+
])
133+
->expectsQuestion('Which panel would you like to create this user in?', 'admin')
134+
->assertSuccessful();
135+
136+
expect(User::query()->where('email', 'dan@filamentphp.com')->exists())
137+
->toBeTrue();
138+
});

0 commit comments

Comments
 (0)