diff --git a/config/filament.php b/config/filament.php index 96978ce035..c806e64d0d 100644 --- a/config/filament.php +++ b/config/filament.php @@ -15,12 +15,12 @@ return [ */ 'features' => [ - Features::users(), + Features::registration(), ], 'models' => [ - // 'model' => App\Models\Model::class, + 'user' => config('auth.providers.users.model'), ], @@ -80,6 +80,7 @@ return [ 'login' => Filament\Http\Livewire\Auth\Login::class, 'forgot-password' => Filament\Http\Livewire\Auth\ForgotPassword::class, 'reset-password' => Filament\Http\Livewire\Auth\ResetPassword::class, + 'register' => Filament\Http\Livewire\Auth\Register::class, 'dashboard' => Filament\Http\Livewire\Dashboard::class, ], diff --git a/resources/lang/en/auth.php b/resources/lang/en/auth.php index 239fd4c8e6..51059f21ba 100644 --- a/resources/lang/en/auth.php +++ b/resources/lang/en/auth.php @@ -15,4 +15,6 @@ return [ 'signin' => 'Sign in to your account', + 'register' => 'Create a new account', + ]; diff --git a/resources/views/livewire/auth/login.blade.php b/resources/views/livewire/auth/login.blade.php index 83e54a95bf..ef820a1dd9 100644 --- a/resources/views/livewire/auth/login.blade.php +++ b/resources/views/livewire/auth/login.blade.php @@ -1,6 +1,13 @@
+ @if (Route::has('filament.register')) + + + {{ __('filament::auth.register') }} + + + @endif diff --git a/resources/views/livewire/auth/register.blade.php b/resources/views/livewire/auth/register.blade.php new file mode 100644 index 0000000000..71b4fabb4d --- /dev/null +++ b/resources/views/livewire/auth/register.blade.php @@ -0,0 +1,27 @@ + + + + + + ← {{ __('Back to login') }} + + + + + + + + + + + + + + + + + + + {{ __('Register') }} + + \ No newline at end of file diff --git a/routes/web.php b/routes/web.php index 1b205bbd3c..94a25aadfd 100644 --- a/routes/web.php +++ b/routes/web.php @@ -24,23 +24,15 @@ Route::group([ Route::get('/forgot-password', config('filament.livewire.forgot-password'))->name('password.forgot'); Route::get('/reset-password/{token}', config('filament.livewire.reset-password'))->name('password.reset'); + // Registration + if (Features::registersUsers()) { + Route::get('/register', config('filament.livewire.register'))->name('register'); + } + // Authenticated routes Route::group(['middleware' => ['auth', 'verified']], function () { // Dashboard Route::get('/', config('filament.livewire.dashboard'))->name('dashboard'); - - /* - // User profile - Route::get('/profile', config('filament.livewire.profile'))->name('profile'); - - // Users - if (Features::managesUsers()) { - Route::name('users.')->group(function () { - Route::get('/users', config('filament.livewire.users'))->name('index'); - Route::get('/user/{user}', config('filament.livewire.user'))->name('show'); - }); - } - */ }); }); diff --git a/src/Contracts/User.php b/src/Contracts/User.php new file mode 100644 index 0000000000..504e5478b0 --- /dev/null +++ b/src/Contracts/User.php @@ -0,0 +1,8 @@ +app->config['filament.models']; + $models = $this->app->config['filament.models']; - if (! $config) { + if (! $models) { return; } - // $this->app->bind(ModelContract::class, $config['model']); + $this->app->bind(UserContract::class, $models['user']); } protected function bootPolicies(): void diff --git a/src/Http/Livewire/Auth/Register.php b/src/Http/Livewire/Auth/Register.php new file mode 100644 index 0000000000..d1864a5ceb --- /dev/null +++ b/src/Http/Livewire/Auth/Register.php @@ -0,0 +1,47 @@ + 'required|min:2', + 'email' => 'required|email|unique:users', + 'password' => 'required|min:8|confirmed', + 'password_confirmation' => 'required|same:password', + ]; + + public function updatedEmail($value) + { + $this->validateOnly('email', ['email' => 'unique:users']); + } + + public function register() + { + $this->validate(); + $userClass = app(UserContract::class); + $user = $userClass::create([ + 'name' => $this->name, + 'email' => $this->email, + 'password' => Hash::make($this->password), + ]); + Auth::login($user); + return redirect()->route('filament.dashboard'); + } + + public function render() + { + return view('filament::livewire.auth.register') + ->layout('filament::layouts.auth', ['title' => __('filament::auth.register')]); + } +} \ No newline at end of file diff --git a/src/Http/Livewire/Auth/ResetPassword.php b/src/Http/Livewire/Auth/ResetPassword.php index 85900499eb..f0ef6c942b 100644 --- a/src/Http/Livewire/Auth/ResetPassword.php +++ b/src/Http/Livewire/Auth/ResetPassword.php @@ -24,7 +24,7 @@ class ResetPassword extends Component protected $rules = [ 'email' => 'required|email', 'password' => 'required|min:8|confirmed', - 'password_confirmation' => 'required', + 'password_confirmation' => 'required|same:password', ]; public function mount(Request $request, $token) diff --git a/tests/Database/Models/User.php b/tests/Database/Models/User.php index 34b3552d8b..944dae5771 100644 --- a/tests/Database/Models/User.php +++ b/tests/Database/Models/User.php @@ -14,10 +14,25 @@ class User extends Authenticatable use HasFactory, Notifiable, FilamentUser; /** - * Disable Laravel's mass assignment protection + * The attributes that are mass assignable. + * * @var array */ - protected $guarded = []; + protected $fillable = [ + 'name', + 'email', + 'password', + ]; + + /** + * The attributes that should be hidden for arrays. + * + * @var array + */ + protected $hidden = [ + 'password', + 'remember_token', + ]; /** * The attributes that should be cast to native types. diff --git a/tests/Feature/ForgotPasswordTest.php b/tests/Feature/ForgotPasswordTest.php index 7afbbbcf10..94481ac702 100644 --- a/tests/Feature/ForgotPasswordTest.php +++ b/tests/Feature/ForgotPasswordTest.php @@ -1,6 +1,6 @@ get(route('filament.register')) + ->assertSuccessful() + ->assertSee(__('filament::auth.register')); + } + + public function test_can_register() + { + Livewire::test(Register::class) + ->set('name', 'Me') + ->set('email', 'me@example.com') + ->set('password', 'password') + ->set('password_confirmation', 'password') + ->call('register') + ->assertHasNoErrors(['name', 'email', 'password', 'password_confirmation']) + ->assertRedirect(route('filament.dashboard')); + + $this->assertDatabaseHas('users', [ + 'name' => 'Me', + 'email' => 'me@example.com', + ]); + + $this->assertAuthenticated(); + } + + public function test_name_is_required() + { + Livewire::test(Register::class) + ->call('register') + ->assertHasErrors(['name' => 'required']); + } + + public function test_name_is_minimum_two_characters() + { + Livewire::test(Register::class) + ->set('name', 'a') + ->call('register') + ->assertHasErrors(['name' => 'min']); + } + + public function test_email_is_required() + { + Livewire::test(Register::class) + ->call('register') + ->assertHasErrors(['email' => 'required']); + } + + public function test_email_is_valid_email() + { + Livewire::test(Register::class) + ->set('email', 'Something') + ->call('register') + ->assertHasErrors(['email' => 'email']); + } + + public function test_email_is_unique() + { + User::factory()->create(['email' => 'test@example.com']); + + Livewire::test(Register::class) + ->set('email', 'test@example.com') + ->assertHasErrors(['email' => 'unique']); + } + + public function test_password_is_required() + { + Livewire::test(Register::class) + ->call('register') + ->assertHasErrors(['password' => 'required']); + } + + public function test_password_is_minimum_eight_characters() + { + Livewire::test(Register::class) + ->set('password', 'test') + ->call('register') + ->assertHasErrors(['password' => 'min']); + } + + public function test_password_must_be_confirmed() + { + Livewire::test(Register::class) + ->set('password', 'test') + ->call('register') + ->assertHasErrors(['password' => 'confirmed']); + } + + public function test_password_confirmation_is_required() + { + Livewire::test(Register::class) + ->call('register') + ->assertHasErrors(['password_confirmation' => 'required']); + } + + public function test_password_confirmation_matches_password() + { + Livewire::test(Register::class) + ->set('password', 'test') + ->set('password_confirmation', 'test2') + ->call('register') + ->assertHasErrors(['password_confirmation' => 'same']); + } +} \ No newline at end of file diff --git a/tests/Feature/ResetPasswordTest.php b/tests/Feature/ResetPasswordTest.php index 8ddb41b148..12ddd9cf9a 100644 --- a/tests/Feature/ResetPasswordTest.php +++ b/tests/Feature/ResetPasswordTest.php @@ -1,15 +1,15 @@ set('auth.providers.users.model', User::class); + $app['config']->set('filament.models.user', User::class); } } \ No newline at end of file