Layout, component, slot, stack — semua alat sudah ada di meja kita. Chapter penutup ini merakitnya menjadi strategi: kapan memakai @include, kapan component anonymous, kapan perlu class component. Pada praktiknya kita bangun sebuah UI kit kecil yang benar-benar teruji — tombol dengan variant, kartu yang menampung konten apa pun, dan input yang otomatis mengurus old() serta pesan validasi. Pola-pola inilah yang membuat sepuluh halaman berikutnya ditulis cepat tanpa HTML yang disalin-tempel.

Memilih Strategi Potongan Tampilan #

Tiga mekanisme reusable di Blade punya wilayah masing-masing. Tabel berikut merangkum batasnya:

Aspek@includeAnonymous componentClass component
Cara panggil@include('partials.x', [...])<x-nama><x-nama>
Kontrak propertiTidak ada — data bebas@props dengan defaultConstructor bertipe
Konten turunanTidak ada$slot + named slotSama seperti anonymous
Atribut HTMLTidak ada$attributes bag$attributes bag
Logika kompleksTidak layak@php ringkasClass PHP penuh, bisa diuji
Pas untukPotongan statis, layout emailUI kit sehari-hariComponent berquery atau berhitung

Aturan praktisnya: mulai dari anonymous component untuk semua potongan visual. Naikkan ke class component saat component butuh logika non-trivial — query, perhitungan, dependency injection. Sisakan @include untuk potongan sederhana yang tidak butuh kontrak ketat.

Kit Kecil dalam Folder ui #

Konvensi penamaan menentukan keterbacaan jangka panjang. Semua tag component berprefix x-, berkasnya di resources/views/components/, dan subfolder menjadi bagian nama: components/ui/button.blade.php dipanggil sebagai <x-ui.button>. Gaya penamaan ui.* ini lazim dipakai untuk kit internal sebuah aplikasi. Properti multi-kata mengikuti kesepakatan HTML: ditulis kebab-case pada tag, dibaca camelCase di @props — atribut nilai-default="Nilai awal" mengisi prop $nilaiDefault, kita verifikasi pemetaannya bekerja tepat seperti itu pada sandbox. Tombolnya memakai pola variant — prop string dipetakan menjadi class CSS:

blade
{{-- resources/views/components/ui/button.blade.php --}}
@props([
    'variant' => 'primary',
    'type' => 'button',
])

@php
    $kelas = [
        'primary' => 'btn btn-primary',
        'secondary' => 'btn btn-outline',
        'danger' => 'btn btn-danger',
    ][$variant] ?? 'btn';
@endphp

<button type="{{ $type }}" {{ $attributes->merge(['class' => $kelas]) }}>
    {{ $slot->isEmpty() ? 'Tombol' : $slot }}
</button>

Pemetaan array di blok @php menjaga daftar variant tetap terlihat di satu tempat. Caller tidak pernah menulis class CSS mentah — cukup variant="danger" — sehingga perubahan tema hanya menyentuh satu berkas.

Komposisi: Card Menampung Apa Pun #

Kartu adalah contoh murni composition: komponen menyediakan kerangka, isi datang dari slot. Versi kit kita:

blade
{{-- resources/views/components/ui/card.blade.php --}}
@props(['heading' => null])

<div {{ $attributes->merge(['class' => 'card']) }}>
    @isset($heading)
        <div class="card-header">{{ $heading }}</div>
    @endisset
    <div class="card-body">
        {{ $slot }}
    </div>
</div>

Halaman resources/views/kit.blade.php merangkai ketiga component — kartu membungkus form, form memakai input kit, tombol menempel di dalamnya:

blade
<x-ui.card heading="Tambah Tugas Cepat">
    <form method="POST" action="{{ route('tugas.simpan') }}">
        @csrf

        <x-ui.input nama="judul" label="Judul" placeholder="cth: Belajar Blade" />
        <x-ui.input nama="deskripsi" label="Deskripsi" />

        <div class="aksi">
            <x-ui.button variant="secondary">Batal</x-ui.button>
            <x-ui.button type="submit">Simpan</x-ui.button>
        </div>
    </form>
</x-ui.card>

<x-ui.card>
    <x-ui.button variant="danger">Hapus Semua Tugas</x-ui.button>
</x-ui.card>

Komposisi seperti ini berjalan satu arah: kartu tidak tahu apa isi card-body-nya, tombol tidak tahu ia berada di dalam kartu. Setiap component hanya mengurus tampilan dan kontraknya sendiri, sehingga bebas disusun ulang — halaman bisa menukar kartu dengan elemen lain, atau memakai tombol yang sama di luar kartu, tanpa satu perubahan pun di berkas component.

Input dengan old() dan @error Terpasang #

Component input adalah tempat pola form dari chapter Forms dan Old Input berubah jadi satu kali tulis. Label, pengisian ulang old(), dan pesan @error hidup di dalam component — halaman tinggal menyebut nama field:

blade
{{-- resources/views/components/ui/input.blade.php --}}
@props([
    'nama' => null,
    'label' => null,
    'type' => 'text',
    'nilaiDefault' => null,
])

<label for="{{ $nama }}">{{ $label ?? $nama }}</label>
<input
    id="{{ $nama }}"
    type="{{ $type }}"
    name="{{ $nama }}"
    value="{{ old($nama, $nilaiDefault) }}"
    {{ $attributes->only(['class', 'placeholder']) }}
>
@error($nama)
    <p class="field-error">{{ $message }}</p>
@enderror

Bagian penting dari render awal GET /kit — kondisi bersih tanpa old input:

text
<div class="card">
            <div class="card-header">Tambah Tugas Cepat</div>
        <div class="card-body">
        <form method="POST" action="http://localhost/tugas">
        <input type="hidden" name="_token" value="(token sesi acak)" autocomplete="off">
        <label for="judul">Judul</label>
<input
    id="judul"
    type="text"
    name="judul"
    value=""
    placeholder="cth: Belajar Blade"
>
        <div class="aksi">
            <button type="button" class="btn btn-outline">
    Batal
</button>
            <button type="submit" class="btn btn-primary">
    Simpan
</button>
        </div>
    </form>
    </div>
</div>

Variant bekerja di tombol (btn-outline, btn-primary), placeholder lolos lewat penyaringan $attributes->only(), dan nilai input kosong karena old() tidak menyimpan apa pun.

Kondisi Error Teruji Ujung ke Ujung #

Setelah submit dengan judul 'AB' ditolak validasi, halaman yang sama otomatis berubah — semua tanpa satu baris pun ditambah di halaman:

text
        <label for="judul">Judul</label>
<input
    id="judul"
    type="text"
    name="judul"
    value="AB"
    placeholder="cth: Belajar Blade"
>
    <p class="field-error">The judul field must be at least 3 characters.</p>
        <label for="deskripsi">Deskripsi</label>
<input
    id="deskripsi"
    type="text"
    name="deskripsi"
    value="Isian dari komponen ui."

>

Nilai lama terisi kembali lewat old($nama, $nilaiDefault) dan pesan error muncul lewat @error($nama) — keduanya mengunci field yang tepat karena sama-sama mengacu pada nama field. Feature test-nya memastikan perilaku ini dari luar:

php
public function test_input_kit_menangani_old_dan_error_otomatis(): void
{
    $this->get('/kit'); // halaman asal redirect back = halaman kit

    $response = $this->followingRedirects()->post(route('tugas.simpan'), [
        'judul' => 'AB',
        'deskripsi' => 'Isian dari komponen ui.',
    ]);

    $response->assertSee('value="AB"', false);
    $response->assertSee('The judul field must be at least 3 characters.');
}

Output yang Dihasilkan:

text
PASS  Tests\Feature\UiKitTest
✓ kit merender komposisi dan variant                     0.10s
✓ input kit menangani old dan error otomatis              0.02s

Tests:    2 passed (9 assertions)
Duration: 0.20s

Dengan kit ini, section View dan Blade tertutup: kita bisa mengirim data ke view, menyusun layout, membangun component, mengelola slot dan atribut, mengamankan form, sampai merakit antarmuka reusable. Perjalanan berikutnya berpindah ke lapisan data — bagaimana request divalidasi secara menyeluruh di sisi server, materi section Validasi dan Form .