Filament 4 : Login Menggunakan Username atau Email

Dalam pembuatan sebuah aplikasi tak jarang kita membutuhkan fitur log in menggunakan username atau email secara bersamaan. Dalam kasus Filament, fitur log in bawaannya adalah menggunakan email sebagai credential.
Bagaimana cara mengkustomisasi fitur login bawaan Filament ini agar sesuai kebutuhan kita untuk bisa melakukan log in dengan menggunakan username ataupun email secara bersamaan ? Kita pelajari bersama di sini :p.
Anggaplah di sini posisinya, kita sudah punya aplikasi Filament yang sudah berjalan ataupun sedang proses development, maka pertama kali kita butuh untuk menambahkan kolom username pada tabel users.
Menambahkan Kolom Username#
Pertama buat migration, jalankan perintah di bawah ini.
php artisan make:migration add_username_to_users_table --table=users
Kemudian, perbarui kode pada fungsi up():
public function up(): void
{
Schema::table('users', function (Blueprint $table) {
$table->string('username')->after('email')->unique();
});
}
Kita, juga bisa menambahkan nullable(), jika pada aplikasi yang saat ini sudah berjalan dan sudah memiliki pengguna.
Kemudian jalankan migration dengan perintah ini:
php artisan migrate
Kemudian pada contoh ini saya akan menggunakan factory untuk menambahkan user untuk melakukan pengujian pada fitur log in nantinya.
Pada berkas database/factories/UserFactory.php saya akan tambahkan baris kode untuk menangani kolom username.
public function definition(): array
{
return [
'name' => fake()->name(),
'email' => fake()->unique()->safeEmail(),
'username' => fake()->userName(),
'email_verified_at' => now(),
'password' => static::$password ??= Hash::make('password'),
'remember_token' => Str::random(10),
];
}
Setelah itu, kita tambahkan pengguna menggunakan tinker.
php artisan tinker
Pada CLI Tinker, jalankan kode berikut.
App\Models\User::factory()->create(['name' => 'Amirul', 'username' => 'amirul','email' => 'amirul@kawankoding.com']);
Kurang lebih keluarannya seperti gambar di bawah ini jika berhasil, artinya kita sudah punya seorang pengguna dengan username.

Kustomisasi Fitur Login Filament#
Selesai menyiapkan kebutuhan, kita lanjut untuk melakukan kustomisasi pada fitur log in di Filament, semua pengaturan Panel Filament berada dalam Service Provider. Pada contoh ini kita menggunakan AdminPanelServiceProvider bawaan. Di sini kita memiliki fungsi login() yang menerima sebuah class sebagai parameter.
class AdminPanelProvider extends PanelProvider
{
public function panel(Panel $panel): Panel
{
return $panel
->default()
->id('admin')
->path('admin')
->login()
->colors([
'primary' => Color::Amber,
])
// ....
}
}
Mari kita buat sebuah halaman Login kita sendiri dan kita suntikkan ke dalam method login(). Saya akan buat class di dalam direktori App\Filament\Auth. Ini membutuhkan extend ke class Login yang asli dari Filament.
<?php
namespace App\Filament\Auth;
use Filament\Auth\Pages\Login as PagesLogin;
class Login extends PagesLogin
{
// ..
}
Kita bisa melihat sumber kode penuh dari class Login FIlament Repositori Github, untuk kasus ini, kita akan override tiga method.
form- untuk menampilkan form yang berbeda.getCredentialsFromFormData()- untuk memproses data dari formulir dengan benarthrowFailureValidationException()- untuk menampilkan pesan error dengan benar.
Kemudian kita juga akan mengganti method getEmailFormComponent dengan getLoginFormComponent buatan kita sendiri, untuk menggunakan kolom selain email. Kita akan beri nama kolom ini login yang akan menerima nilai username atau email.
app/Filament/Auth/Login.php :
use Filament\Auth\Pages\Login as PagesLogin;
use Filament\Forms\Components\TextInput;
use Filament\Schemas\Components\Component;
use Filament\Schemas\Schema;
class Login extends PagesLogin
{
public function form(Schema $schema): Schema
{
return $schema
->components([
// $this->getEmailFormComponent(),
$this->getLoginFormComponent(),
$this->getPasswordFormComponent(),
$this->getRememberFormComponent(),
]);
}
protected function getLoginFormComponent(): Component
{
return TextInput::make('login')
->label('Username / Email')
->required()
->autocomplete()
->autofocus()
->extraInputAttributes(['tabindex' => 1]);
}
}
Kemudian, perbarui method login() pada AdminPanelServiceProvider.
use App\Filament\Auth\Login;
class AdminPanelProvider extends PanelProvider
{
public function panel(Panel $panel): Panel
{
return $panel
->default()
->id('admin')
->path('admin')
->login(Login::class)
// ....
}
}
Lalu, kita akan mendapatkan tampilan seperti ini.

Sekarang, mari kita timpa method getCredentialsFromFormData() untuk menggunakan username atau email untuk autentikasi.
class Login extends PagesLogin
{
// ...
protected function getCredentialsFromFormData(array $data): array
{
$loginType = filter_var($data['login'], FILTER_VALIDATE_EMAIL ) ? 'email' : 'username';
return [
$loginType => $data['login'],
'password' => $data['password'],
];
}
}
Pada method ini kita menggunakan fungsi PHP fileter_var untuk mengecek jika nilai yang dimasukkan apakah sebuah email. atau bukan. Berdasarkan hal tersebut, kita memberi nilai pada variabel $loginType dengan email atau username untuk digunakan pengecekan ke database.
Terakhir, ketika ada kesalahan atau ketidakcocokan pada credential yang kita masukkan, maka pesan error tidak akan tampil. Ini dikarenakan validasinya menggunakan kolom data.email dan bukan data.login yang sudah kita baut tadi. Artinya pesan error tidak akan muncul karena tidak cocok. Untuk membereskan hal ini kita perlu menimpa lagi satu method throwFailureValidationException()
class Login extends PagesLogin
{
// ...
protected function throwFailureValidationException(): never
{
throw ValidationException::withMessages([
'data.login' => __('filament-panels::auth/pages/login.messages.failed'),
]);
}
}
Sekarang, jika kita ada kesalahan dalam proses autentikasi / log in, akan seperti ini tampilannya:

Kelar ! sekarang kita bisa log in dengan username atau email dengan kolom yang ama di Filament 4.