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 | @include | Anonymous component | Class component |
|---|---|---|---|
| Cara panggil | @include('partials.x', [...]) | <x-nama> | <x-nama> |
| Kontrak properti | Tidak ada — data bebas | @props dengan default | Constructor bertipe |
| Konten turunan | Tidak ada | $slot + named slot | Sama seperti anonymous |
| Atribut HTML | Tidak ada | $attributes bag | $attributes bag |
| Logika kompleks | Tidak layak | @php ringkas | Class PHP penuh, bisa diuji |
| Pas untuk | Potongan statis, layout email | UI kit sehari-hari | Component 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:
{{-- 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:
{{-- 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:
<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:
{{-- 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>
@enderrorBagian penting dari render awal GET /kit — kondisi bersih tanpa old input:
<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:
<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:
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:
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.20sDengan 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 .