Sistem authentication yang rapi tetap butuh bahan bakar: user. Kecuali seluruh akun dibuat lewat seeder, pintu masuk data pengguna yang sesungguhnya adalah alur registrasi — dan alur ini menyentuh hampir semua fondasi yang sudah kita pelajari: routing, validasi, Eloquent, sampai CSRF. Di chapter ini kita rakit alur tersebut dari nol: form registrasi, validasi data masuk, penyimpanan password dalam bentuk hash, event pendaftaran, sampai login otomatis begitu akun terbentuk. Semua kode di sini telah diuji penuh lewat feature test di akhir chapter.

Route untuk Registrasi #

Dua route menangani satu alur: GET menampilkan form, POST memproses data. Keduanya kita daftarkan di routes/web.php:

php
use App\Http\Controllers\RegisterController;

Route::get('/register', [RegisterController::class, 'create'])->name('register');
Route::post('/register', [RegisterController::class, 'store'])->name('register.store');

Output route:list memastikan keduanya terdaftar dengan controller dan named route yang benar:

bash
php artisan route:list --path=register

Output yang Dihasilkan:

text
  GET|HEAD   register ................... register › RegisterController@create
  POST       register .............. register.store › RegisterController@store

                                                            Showing [2] routes

Form Registrasi di Blade #

Form disimpan sebagai resources/views/auth/register.blade.php. Isinya HTML biasa dengan tiga bumbu Laravel yang wajib ada:

blade
<!DOCTYPE html>
<html lang="id">
<head>
    <meta charset="utf-8">
    <title>Registrasi</title>
</head>
<body>
    <h2>Registrasi Pengguna</h2>

    <form method="POST" action="{{ route('register.store') }}">
        @csrf

        <label for="name">Nama</label>
        <input id="name" type="text" name="name" value="{{ old('name') }}" required autofocus>

        @error('name')
            <p>{{ $message }}</p>
        @enderror

        <label for="email">Email</label>
        <input id="email" type="email" name="email" value="{{ old('email') }}" required>

        @error('email')
            <p>{{ $message }}</p>
        @enderror

        <label for="password">Password</label>
        <input id="password" type="password" name="password" required>

        @error('password')
            <p>{{ $message }}</p>
        @enderror

        <label for="password_confirmation">Konfirmasi Password</label>
        <input id="password_confirmation" type="password" name="password_confirmation" required>

        <button type="submit">Daftar</button>
    </form>
</body>
</html>

Directive @csrf menyisipkan token CSRF ke dalam form — tanpa token tersebut, Laravel menolak seluruh POST request; detailnya ada di chapter CSRF Protection . Helper old('name') mengisi ulang isian lama saat validasi gagal, dan @error menampilkan pesan error di bawah field yang bermasalah — pola yang persis sama dengan yang kita bahas di section validasi, khususnya Validasi Unique dan Exists .

Controller: Validasi hingga Login Otomatis #

Method store pada RegisterController adalah inti seluruh alur:

php
<?php

namespace App\Http\Controllers;

use App\Models\User;
use Illuminate\Auth\Events\Registered;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Hash;

class RegisterController extends Controller
{
    public function create()
    {
        return view('auth.register');
    }

    public function store(Request $request)
    {
        $validated = $request->validate([
            'name' => ['required', 'string', 'max:255'],
            'email' => ['required', 'email', 'max:255', 'unique:users,email'],
            'password' => ['required', 'string', 'min:8', 'confirmed'],
        ]);

        $user = User::create([
            'name' => $validated['name'],
            'email' => $validated['email'],
            'password' => Hash::make($validated['password']),
        ]);

        event(new Registered($user));

        Auth::login($user);

        return redirect()
            ->route('dashboard')
            ->with('status', 'Registrasi berhasil, selamat datang!');
    }
}

Kita bedah baris demi baris method store:

  1. Validasi — unique:users,email menolak email yang sudah terdaftar di tabel users, sedangkan confirmed mencocokkan field password dengan password_confirmation: Laravel otomatis mencari field bernama sama plus akhiran _confirmation, jadi nama field di form harus mengikuti konvensi tersebut. Rule min:8 sendiri adalah kebijakan minimum yang wajar untuk password modern. Validasi gagal otomatis me-redirect balik lengkap dengan error dan old input, tanpa satu baris pun tambahan.
  2. Hash::make — password di-hash sebelum menyentuh database. Plaintext tidak pernah disimpan; alasan dan mekanisme hash-nya dibedah tuntas di chapter Password Hashing .
  3. User::create — menyimpan user baru lewat mass assignment pada tiga kolom yang memang fillable.
  4. event(new Registered($user)) — Laravel mengirim event bawaan ini setiap pendaftaran; listener pengiriman email verifikasi atau penyambutan bisa berlangganan ke event ini tanpa menyentuh controller.
  5. Auth::login($user) — mendaftarkan user ke session tanpa memeriksa kredensial lagi; user baru langsung ter-authentication tanpa diminta login. Ini berbeda dari Auth::attempt yang selalu memeriksa pasangan email dan password — bentuk kerabat keduanya dibedah di chapter Login dan Logout .
  6. Redirect — user diantarkan ke route dashboard dengan flash message status.

Route tujuan redirect perlu dilindungi — di sini dashboard memakai middleware auth sehingga hanya user yang terdaftar di session yang bisa membukanya.

Menguji Alur Registrasi #

Feature test membuktikan seluruh rantai di atas, dari HTTP request sampai isi database:

php
<?php

namespace Tests\Feature;

use App\Models\User;
use Illuminate\Auth\Events\Registered;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\Event;
use Illuminate\Support\Facades\Hash;
use Tests\TestCase;

class RegistrationTest extends TestCase
{
    use RefreshDatabase;

    public function test_user_baru_berhasil_mendaftar(): void
    {
        Event::fake();

        $response = $this->post('/register', [
            'name' => 'Budi',
            'email' => '[email protected]',
            'password' => 'rahasia-kuat',
            'password_confirmation' => 'rahasia-kuat',
        ]);

        $response->assertRedirect('/dashboard');
        $this->assertDatabaseHas('users', [
            'name' => 'Budi',
            'email' => '[email protected]',
        ]);
        $this->assertAuthenticated();

        $hash = User::where('email', '[email protected]')->value('password');
        $this->assertStringStartsWith('$2y$', $hash);
        $this->assertTrue(Hash::check('rahasia-kuat', $hash));

        Event::assertDispatched(Registered::class);
    }
}

Test ini sekaligus membuktikan tiga klaim penting: redirect menuju dashboard, baris user muncul di database, dan kolom password berisi hash bcrypt (diawali $2y$) yang lolos Hash::check — bukan plaintext. Event::fake() mencegah event sungguhan dikerjakan pendengarnya, lalu assertDispatched memastikan Registered benar-benar terkirim dari controller. Selain test utama di atas, berkas test yang sama memuat dua skenario pendamping: GET /register harus mengembalikan status 200 (form dan Blade-nya sehat), dan email yang sudah dipakai harus ditolak lewat assertSessionHasErrors('email') dengan status tetap assertGuest(). Berikut hasil eksekusi seluruhnya di sandbox:

bash
php artisan test tests/Feature/RegistrationTest.php

Output yang Dihasilkan:

text
  PASS  Tests\Feature\RegistrationTest
  ✓  halaman registrasi bisa dikunjungi                          0.14s
  ✓  user baru berhasil mendaftar                                0.02s
  ✓  email yang sudah dipakai ditolak                            0.02s

  Tests:    3 passed (11 assertions)
  Duration:  0.26s

Alur registrasi kini hidup: data masuk tervalidasi, password tersimpan sebagai hash, dan user langsung login. Pertanyaan berikutnya adalah pintu yang lebih sering dipakai setelah hari pertama — form login untuk user yang sudah terdaftar, beserta ritual logout yang aman. Semuanya kita bangun di chapter Login dan Logout . Rujukan resmi alur registrasi manual ada di dokumentasi Authentication .