Bayangkan aplikasi dengan tiga puluh halaman yang semuanya memuat navigasi, tag <head>, dan footer yang sama. Menyalin-tempel kerangka HTML ke setiap view membuat perubahan kecil — satu item menu baru, misalnya — harus diulang tiga puluh kali. Template inheritance menyelesaikan masalah ini: satu berkas layout menyimpan kerangka bersama, sementara setiap halaman anak cukup mengisi bagian yang berbeda. Berbeda dari @include yang menyisipkan bagian kecil berulang-ulang ke dalam satu halaman, inheritance bekerja dari arah sebaliknya — halaman anak mengambil alih seluruh kerangka lalu mengisi slot-slot yang disediakan. Mekanismenya dikerjakan tiga directive — @extends, @section, dan @yield — dan chapter ini mengujinya dengan satu layout serta dua halaman anak yang benar-benar dirender lewat HTTP.
Layout Induk dengan @yield #
Layout adalah view biasa yang menyisipkan “lubang” bernama di titik-titik yang bisa diisi halaman anak. Directive @yield('nama') menandai lubang tersebut, dan argumen keduanya menjadi nilai default bila tidak ada anak yang mengisinya. Buat berkas resources/views/layouts/app.blade.php:
<!DOCTYPE html>
<html lang="id">
<head>
<meta charset="utf-8">
<title>@yield('title', 'Aplikasi Tugas')</title>
</head>
<body>
<nav>Menu: Beranda | Tugas</nav>
<main>
@section('konten')
(konten belum diisi)
@show
</main>
<footer>
@section('footer')
© 2026 Aplikasi Tugas
@show
</footer>
</body>
</html>Dua pola berbeda terlihat di sini. @yield('title', 'Aplikasi Tugas') adalah slot murni: isinya sepenuhnya datang dari halaman anak, atau default-nya dipakai bila anak diam. Adapun pasangan @section('konten') ... @show mendefinisikan konten default langsung di layout — bloknya langsung ditampilkan, tetapi anak boleh menggantinya maupun menambahkannya dengan @parent, seperti terlihat pada footer nanti. Pilihan @yield atau @show menjadi gaya tim: sebagian proyek memakai @yield di semua titik, sebagian memilih @show saat default-nya layak tampil.
Halaman Anak dengan @extends #
Halaman anak tidak memuat kerangka HTML sama sekali. Directive @extends('layouts.app') menyatakan layout yang diwarisi — nama view memakai dot notation, sama seperti helper view(). Isi section dikirim dengan dua bentuk: satu baris untuk nilai pendek, blok @section ... @endsection untuk HTML panjang. Timpa resources/views/tugas/index.blade.php:
@extends('layouts.app')
@section('title', 'Daftar Tugas')
@section('konten')
<h1>Daftar Tugas</h1>
<ul>
@foreach ($tugas as $t)
<li>{{ $t['judul'] }} — {{ $t['status'] }}</li>
@endforeach
</ul>
@endsectionRoute /tugas dari chapter Pengenalan View masih memanggil view ini dengan data yang sama. Hasil request HTTP-nya kini merupakan gabungan layout dan anak:
=== GET /tugas → HTTP 200 ===
<!DOCTYPE html>
<html lang="id">
<head>
<meta charset="utf-8">
<title>Daftar Tugas</title>
</head>
<body>
<nav>Menu: Beranda | Tugas</nav>
<main>
<h1>Daftar Tugas</h1>
<ul>
<li>Belajar Blade — selesai</li>
<li>Menulis migration — proses</li>
</ul>
</main>
<footer>
© 2026 Aplikasi Tugas
</footer>
</body>
</html>Perhatikan apa yang terjadi: <title> berisi Daftar Tugas dari anak (bukan default), section konten berisi daftar tugas hasil @foreach, sedangkan footer memakai default layout karena anak tidak mengisinya. Satu sumber kebenaran untuk seluruh kerangka, isi per halaman.
Menambah Isi dengan @parent #
Kadang anak tidak ingin mengganti section default, melainkan menambah di belakangnya. Directive @parent di dalam @section menyisipkan isi default layout lalu menuliskan tambahan setelahnya. Halaman kedua resources/views/tentang.blade.php memakainya di footer:
@extends('layouts.app')
@section('title', 'Tentang')
@section('konten')
<h1>Tentang Aplikasi</h1>
<p>Aplikasi tugas sederhana untuk belajar Laravel.</p>
@endsection
@section('footer')
@parent
Dibangun dengan Laravel 13.
@endsectionOutput yang Dihasilkan (bagian akhir GET /tentang):
<footer>
© 2026 Aplikasi Tugas
Dibangun dengan Laravel 13.
</footer>
</body>
</html>Footer kini memuat default © 2026 Aplikasi Tugas ditambah baris milik halaman tentang — tanpa anak menulis ulang teks default tersebut.
Menguji Halaman yang Mewarisi Layout #
Halaman hasil inheritance tetap response HTTP biasa, jadi feature test mengujinya seperti biasa. Assertion assertSee memeriksa bahwa bagian layout dan bagian anak sama-sama hadir di output:
public function test_halaman_tugas_mewarisi_layout(): void
{
$response = $this->get('/tugas');
$response->assertStatus(200);
$response->assertSee('<title>Daftar Tugas</title>', false);
$response->assertSee('Menu: Beranda | Tugas');
$response->assertSee('Belajar Blade — selesai');
}Output yang Dihasilkan:
PASS Tests\Feature\LayoutTest
✓ halaman tugas mewarisi layout 0.09s
✓ halaman tentang menambah footer 0.01s
Tests: 2 passed (8 assertions)
Duration: 0.18sArgumen kedua false pada assertSee mematikan escaping, sehingga tag <title> dicocokkan secara harfiah terhadap HTML mentah response. Dua test method menguji kedua halaman: satu memeriksa title dan isi daftar tugas, satu lagi memeriksa hasil @parent pada footer.
Struktur @extends/@section/@yield sudah berumur panjang dan tetap andal. Pola yang lebih modern — component berbasis class dengan x- tag — memperluas ide serupa untuk bagian yang lebih kecil, dan menjadi materi lanjutan pada chapter Layouts dan Sections serta Components.