BAB 10: Views

Memisahkan tampilan dari logika menggunakan sistem view Laravel dan cara passing data ke view.

Response HTML yang kita kirim dari controller di Bab 9 berasal dari view. Kita sudah beberapa kali menulis return view('catatan.index', [...]) tanpa membedah apa yang sebenarnya terjadi. View adalah lapisan presentasi dalam arsitektur MVC — tempat HTML dihasilkan, terpisah rapi dari logika controller yang mengambil data.

Pemisahan ini bukan sekadar gaya penulisan. Controller yang tidak tercampur HTML lebih mudah diuji. View yang tidak mengandung logika bisnis lebih mudah dimodifikasi oleh siapa saja yang hanya berurusan dengan tampilan. Di bab ini kita akan memahami sistem view Laravel secara menyeluruh sebelum masuk ke Blade — template engine yang membuatnya terasa jauh lebih ekspresif dari PHP biasa.

Struktur Direktori View

Semua file view disimpan di direktori resources/views/. Laravel menggunakan notasi dot untuk merujuk view yang berada di subdirektori — titik menggantikan garis miring.

resources/
  views/
    catatan/
      index.blade.php      ← dipanggil sebagai 'catatan.index'
      show.blade.php       ← dipanggil sebagai 'catatan.show'
      create.blade.php     ← dipanggil sebagai 'catatan.create'
      edit.blade.php       ← dipanggil sebagai 'catatan.edit'
    layouts/
      app.blade.php        ← dipanggil sebagai 'layouts.app'
    components/
      alert.blade.php      ← dipanggil sebagai 'components.alert'
    welcome.blade.php      ← dipanggil sebagai 'welcome'

Buat view baru menggunakan Artisan:

php artisan make:view catatan.index
php artisan make:view catatan.show

Perintah ini membuat file di path yang sesuai, termasuk membuat subdirektori jika belum ada.

Memanggil View dari Controller

Cara paling umum memanggil view adalah dengan helper view():

<?php
// app/Http/Controllers/CatatanController.php

namespace App\Http\Controllers;

use App\Models\Catatan;
use Illuminate\View\View;

class CatatanController extends Controller
{
    public function index(): View
    {
        $catatanList = Catatan::latest()->paginate(10);

        // Cara 1: array langsung sebagai argumen kedua
        return view('catatan.index', ['catatanList' => $catatanList]);
    }

    public function show(Catatan $catatan): View
    {
        // Cara 2: helper compact() — membuat array dari nama variabel
        return view('catatan.show', compact('catatan'));
    }

    public function create(): View
    {
        // Cara 3: method with() — berguna saat data dibangun bertahap
        return view('catatan.create')
            ->with('judulHalaman', 'Buat Catatan Baru');
    }
}

Ketiga cara menghasilkan hasil yang sama — data dikirim ke view sebagai variabel yang langsung bisa diakses. Pilih yang paling terbaca untuk situasinya: compact() nyaman untuk satu atau dua variabel model, array langsung bagus untuk banyak data sekaligus, dan with() cocok ketika data ditambahkan secara kondisional.

Menerima Data di View

Di dalam file Blade, semua variabel yang dikirim dari controller langsung tersedia:

{{-- resources/views/catatan/index.blade.php --}}

<!DOCTYPE html>
<html lang="id">
<head>
    <meta charset="UTF-8">
    <title>Daftar Catatan</title>
</head>
<body>
    <h1>Catatan Saya</h1>

    @if ($catatanList->isEmpty())
        <p>Belum ada catatan.</p>
    @else
        <ul>
            @foreach ($catatanList as $catatan)
                <li>
                    <a href="{{ route('catatan.show', $catatan) }}">
                        {{ $catatan->judul }}
                    </a>
                </li>
            @endforeach
        </ul>

        {{ $catatanList->links() }}
    @endif
</body>
</html>

Semua sintaks {{ }}, @if, @foreach adalah Blade — template engine yang akan kita bahas detail di bab berikutnya. Untuk saat ini, perhatikan bahwa $catatanList tersedia langsung karena kita meneruskannya dari controller.

Mengecek Keberadaan View

Sebelum memanggil view yang mungkin tidak selalu ada — misalnya template kustom per tenant — cek dulu keberadaannya:

<?php
// app/Http/Controllers/CatatanController.php

use Illuminate\Support\Facades\View;

public function show(Catatan $catatan): \Illuminate\View\View
{
    // Gunakan template kustom jika ada, fallback ke default
    $namaView = View::exists('catatan.show-premium')
        ? 'catatan.show-premium'
        : 'catatan.show';

    return view($namaView, compact('catatan'));
}

Atau gunakan View::first() untuk mencoba beberapa view secara berurutan dan menggunakan yang pertama ditemukan:

use Illuminate\Support\Facades\View;

// Coba template kustom dulu, fallback ke default
return View::first(['catatan.show-kustom', 'catatan.show'], compact('catatan'));

Berbagi Data ke Semua View

Beberapa data perlu tersedia di seluruh view — misalnya informasi user yang sedang login, notifikasi global, atau konfigurasi aplikasi. Alih-alih meneruskan data ini dari setiap controller method, gunakan View::share() di service provider:

<?php
// app/Providers/AppServiceProvider.php

namespace App\Providers;

use Illuminate\Support\Facades\View;
use Illuminate\Support\ServiceProvider;

class AppServiceProvider extends ServiceProvider
{
    public function boot(): void
    {
        // Data ini tersedia di semua view sebagai $namaAplikasi
        View::share('namaAplikasi', config('app.name'));
    }
}

Hati-hati menggunakan View::share() untuk data yang perlu query database — query ini dijalankan di setiap request, termasuk request yang tidak perlu data tersebut. Gunakan View Composer jika data hanya dibutuhkan di view tertentu.

View Composers

View Composer adalah cara yang lebih terstruktur untuk menyuntikkan data ke view tertentu secara otomatis — tanpa harus meneruskannya dari controller.

Bayangkan sidebar yang menampilkan ringkasan statistik catatan: total catatan, catatan hari ini, catatan belum selesai. Daripada meneruskan data ini dari setiap controller yang menggunakan layout dengan sidebar, definisikan sekali di View Composer:

<?php
// app/View/Composers/SidebarComposer.php

namespace App\View\Composers;

use App\Models\Catatan;
use Illuminate\View\View;

class SidebarComposer
{
    public function compose(View $view): void
    {
        $view->with('statistikCatatan', [
            'total'       => Catatan::count(),
            'hariIni'     => Catatan::whereDate('created_at', today())->count(),
            'belumSelesai' => Catatan::where('selesai', false)->count(),
        ]);
    }
}

Daftarkan composer ini di service provider:

<?php
// app/Providers/AppServiceProvider.php

namespace App\Providers;

use App\View\Composers\SidebarComposer;
use Illuminate\Support\Facades\View;
use Illuminate\Support\ServiceProvider;

class AppServiceProvider extends ServiceProvider
{
    public function boot(): void
    {
        // Jalankan SidebarComposer setiap kali view layouts.app dirender
        View::composer('layouts.app', SidebarComposer::class);

        // Atau untuk beberapa view sekaligus
        View::composer(['catatan.index', 'catatan.show'], SidebarComposer::class);
    }
}

Sekarang setiap kali layouts.app dirender, variabel $statistikCatatan otomatis tersedia — tanpa satu baris kode tambahan di controller manapun.

Composer class otomatis menerima dependency injection melalui constructor-nya. Artinya kamu bisa menyuntikkan repository atau service ke dalam composer, persis seperti di controller.

Mengoptimalkan View untuk Production

Blade templates dikompilasi ke PHP murni saat pertama kali dipanggil, lalu hasilnya dicache. Di production, kompilasi semua view sebelum deploy agar tidak ada overhead kompilasi saat request pertama masuk:

# Kompilasi semua Blade template ke cache
php artisan view:cache

# Hapus cache (perlu dijalankan setelah perubahan view)
php artisan view:clear

Dua perintah ini biasanya dijalankan sebagai bagian dari deployment script — view:clear saat deploy dimulai, view:cache setelah semua file baru tersalin.

Latihan

Coba kerjakan latihan berikut menggunakan CatatanController yang sudah kita bangun:

  1. Buat file view lengkap — Buat resources/views/catatan/index.blade.php yang menampilkan daftar catatan. Sertakan kondisi untuk state kosong (“Belum ada catatan”) dan loop untuk menampilkan judul setiap catatan beserta link ke halaman detailnya.

  2. View Composer untuk metadata — Buat MetadataCatatanComposer yang menyuntikkan $totalCatatan dan $catatanTerbaru (5 catatan terakhir) ke view catatan.index. Daftarkan di AppServiceProvider.

  3. Fallback template — Modifikasi method show() agar menggunakan View::first() untuk mencoba catatan.show-detail terlebih dahulu sebelum fallback ke catatan.show. Buat kedua file view dengan konten yang sedikit berbeda untuk memverifikasi mekanisme fallback bekerja.

Penutup Bab

View adalah lapisan yang memisahkan presentasi dari logika — controller menyiapkan data, view menampilkannya. Dengan memahami cara passing data, View Share untuk data global, dan View Composer untuk data yang spesifik per view, kamu sudah punya fondasi yang solid untuk mengelola lapisan tampilan aplikasi Laravel.

Tapi sejauh ini kita hanya menulis HTML biasa di file Blade. Blade sendiri memiliki syntax yang jauh lebih ekspresif: template inheritance untuk menghindari duplikasi layout, direktif untuk kondisi dan loop, komponen yang bisa dipakai ulang, hingga slot untuk konten yang bervariasi. Semua itu yang akan kita kuasai di bab berikutnya.

Referensi

  1. 1Views — Laravel 12.x Documentation
  2. 2Blade Templates — Laravel 12.x Documentation