Setelah memahami alurnya, kini kita berkenalan dengan kosakata validasi Laravel: rules. Sebuah rule adalah nama kondisi yang harus dipenuhi sebuah field agar dianggap valid. Laravel menyediakan lebih dari 90 rule bawaan — dari pengecekan tipe data, format khusus seperti email dan UUID, sampai perbandingan antar-field. Dalam praktik sehari-hari, dua puluh persen rule yang paling sering dipakai menutupi delapan puluh persen kebutuhan. Chapter ini memetakan rule-rule inti tersebut, dua gaya penulisan rules, serta cara menghentikan validasi lebih awal dengan bail.

Memanggil validate() dengan Rules #

Bentuk pemanggilan yang paling umum adalah $request->validate() dengan array rules berupa attribute field dan daftar rulenya:

php
$validated = $request->validate([
    'judul' => 'required|string|min:5|max:100',
    'email' => 'required|email',
    'umur' => 'nullable|integer|min:17',
]);

Key array adalah nama field; nilainya adalah daftar rule yang dipisah tanda pipa (|). Field judul wajib diisi, harus string, minimal 5 karakter, maksimal 100 karakter. Field umur boleh kosong (nullable), tetapi jika diisi harus integer minimal 17.

Gaya Pipe vs Gaya Array #

Aturan yang sama bisa ditulis sebagai array. Kedua gaya berikut identik secara perilaku:

php
// gaya pipe — ringkas
$rules = [
    'judul' => 'required|string|min:5',
];

// gaya array — eksplisit
$rules = [
    'judul' => ['required', 'string', 'min:5'],
];

Gaya pipe cepat ditulis dan mudah dibaca untuk rule sederhana. Gaya array wajib dipakai ketika rule membawa parameter yang mengandung tanda pipa atau koma — misalnya in:forum,blog — dan saat kita menyisipkan objek rule seperti Rule::unique(). Banyak tim akhirnya menyeragamkan gaya array agar konsisten di seluruh codebase. Tidak ada perbedaan performa di antara keduanya; keduanya di-compile menjadi struktur internal yang sama sebelum validator bekerja.

Tabel Rule yang Paling Sering Dipakai #

RuleFungsiContoh gagal
requiredField wajib ada dan tidak kosong'' (string kosong)
nullableField boleh null/tidak dikirim—
stringHarus string123 (integer)
integerHarus integer'abc'
numericHarus angka (integer/float)'1.2x'
booleanHarus boolean/0/1/‘0’/‘1’'ya'
arrayHarus PHP array'teks'
emailFormat alamat email'budi@@example'
min: / max:Batas bawah/ataslihat catatan di bawah
in:Nilai harus salah satu daftarin:forum,blog vs 'twitter'
not_in:Nilai tidak boleh dari daftarnot_in:admin,root vs 'admin'
alpha_dashHanya huruf, angka, -, _'budi 01' (spasi)
confirmedHarus cocok dengan field _confirmationpassword ≠ ulangi
same: / different:Sama/beda dengan field lainsame:password
starts_with:Harus diawali nilai tertentustarts_with:08 vs '812'
regex:Cocok pola regexregex:/^[A-Z]{2}[0-9]{2}$/
uuidFormat UUID'bukan-uuid'

Kita bisa memverifikasi perilaku tersebut langsung lewat Validator facade dengan beberapa kombinasi data dan rule nyata:

Output yang Dihasilkan:

text
=== min/max pada string vs angka ===
string min:5 'abc'                 => GAGAL: The judul field must be at least 5 characters.
string max:3 'abcd'                => GAGAL: The judul field must not be greater than 3 characters.
integer min:5 (nilai 3)            => GAGAL: The umur field must be at least 5.
integer max:100 (nilai 150)        => GAGAL: The umur field must not be greater than 100.

=== tipe data ===
integer 'abc'                      => GAGAL: The umur field must be an integer.
numeric '12.5'                     => LOLOS
boolean '1'                        => LOLOS
array 'teks'                       => GAGAL: The tags field must be an array.
nullable kosong                    => LOLOS

=== email, in, not_in, alpha_dash ===
email 'budi@@example'              => GAGAL: The email field must be a valid email address.
in:forum,blog                      => GAGAL: The selected sumber is invalid.
not_in:admin,root                  => GAGAL: The selected nama is invalid.
alpha_dash 'budi 01'               => GAGAL: The nama field must only contain letters, numbers, dashes, and underscores.

=== confirmed, same, different, starts_with ===
confirmed (beda)                   => GAGAL: The password field confirmation does not match.
same:password                      => GAGAL: The ulang field must match password.
different:lama                     => GAGAL: The baru field and lama must be different.
starts_with:08 '812'               => GAGAL: The telepon field must start with one of the following: 08.

=== regex, uuid ===
regex kode 'ab12'                  => GAGAL: The kode field format is invalid.
uuid tidak valid                   => GAGAL: The id field must be a valid UUID.

Beberapa hal menonjol dari hasil di atas. numeric menerima '12.5' karena rule tersebut mengizinkan angka desimal dalam bentuk string sekalipun. boolean menerima string '1' karena Laravel mengikuti konvensi form HTML yang mengirim segalanya sebagai string. confirmed mengharuskan ada field pendamping bernama persis password_confirmation — nama field utama ditambah _confirmation.

bail: Berhenti di Error Pertama #

Secara bawaan, Laravel menjalankan semua rule pada sebuah field dan mengumpulkan seluruh error-nya. Rule bail di posisi paling depan mengubah perilaku itu: begitu satu rule gagal, rule berikutnya pada field yang sama tidak diperiksa lagi:

php
$v = Validator::make(['judul' => 'a b'], ['judul' => 'bail|required|string|min:5|alpha_dash']);
echo "dengan bail : ", implode(' | ', $v->errors()->all()), "\n";

$v = Validator::make(['judul' => 'a b'], ['judul' => 'required|string|min:5|alpha_dash']);
echo "tanpa bail  : ", implode(' | ', $v->errors()->all()), "\n";

Output yang Dihasilkan:

text
dengan bail : The judul field must be at least 5 characters.
tanpa bail  : The judul field must be at least 5 characters. | The judul field must only contain letters, numbers, dashes, and underscores.

Nilai 'a b' gagal dua rule sekaligus: terlalu pendek untuk min:5 dan mengandung spasi yang dilarang alpha_dash. Dengan bail, pengguna hanya melihat satu pesan yang paling relevan; tanpa bail, keduanya tampil bersamaan. Perlu dicatat bahwa bail bekerja per field, bukan global — field lain tetap divalidasi lengkap. Kombinasi lazimnya adalah menempatkan rule paling murah paling depan (misalnya required sebelum regex:) agar pemeriksaan berhenti sedini mungkin.

Menguji dengan Feature Test #

Perilaku validasi paling meyakinkan diuji lewat feature test yang benar-benar melewati route:

php
public function test_invalid_web_input_redirects_back_with_errors(): void
{
    $response = $this->post('/posts', [
        'judul' => '',
        'isi' => '',
        'email' => 'bukan-email',
    ]);

    $response->assertStatus(302);
    $response->assertSessionHasErrors(['judul', 'isi', 'email']);
}

Assertion assertStatus(302) membuktikan request dibatalkan dan di-redirect, sementara assertSessionHasErrors memastikan ketiga field masuk error bag. Untuk request versi JSON, assertion yang setara adalah $this->postJson(...) diikuti assertStatus(422) dan assertInvalid(['email']) — pola yang akan sering kita pakai di chapter-chapter selanjutnya.

Rule yang telah kita kuasai ditulis menempel di controller. Begitu daftarnya membengkak dan dipakai di beberapa route, waktunya memindahkannya ke class tersendiri — itulah topik FormRequest di chapter berikutnya. Daftar lengkap seluruh rule bawaan bisa dirujuk di dokumentasi Validation .