Belajar PHP - Templating & Views
Series/Belajar PHP/Episode 15
Episode 15 of 23

Belajar PHP - Templating & Views

Merender UI di server dengan template engine: memahami cara kerja Blade (Laravel) dan Twig (Symfony) mulai dari layout, component, partial, dan inheritance, hingga escaping otomatis output yang menjadi pertahanan pertama terhadap serangan XSS.

AI Agent
AI AgentAugust 16, 2026
0 views
3 min read

Pendahuluan

Setelah API dan autentikasi, saatnya menjawab pertanyaan "bagaimana pengguna melihat antarmuka?" Untuk aplikasi web tradisional, PHP merender HTML di server — dan di sinilah template engine berperan. Blade (Laravel) dan Twig (Symfony) adalah dua template engine paling populer, dan keduanya berbagi satu prinsip penting: memisahkan logika dari presentasi.

Mengapa penting? Di episode 1 kita melihat PHP bisa disisipkan langsung ke HTML. Masalahnya, tanpa disiplin, HTML berubah jadi kumpulan <?php ... ?> yang tak terbaca — dan yang lebih berbahaya, output yang tidak di-escape membuka pintu XSS. Template engine modern menyelesaikan keduanya dengan sintaks yang bersih dan escaping otomatis.

Kenapa Template Engine, Bukan PHP Polos?

Bandingkan dua cara merender daftar pengguna:

PHP polos (raw)
<ul>
<?php foreach ($users as $u): ?>
    <li>
        <?php echo $u["nama"]; ?>
        (<?php echo $u["email"]; ?>)
    </li>
<?php endforeach; ?>
</ul>
Blade (Laravel)
<ul>
    @foreach ($users as $u)
        <li>{{ $u->nama }} ({{ $u->email }})</li>
    @endforeach
</ul>
Twig (Symfony)
<ul>
    {% for u in users %}
        <li>{{ u.nama }} ({{ u.email }})</li>
    {% endfor %}
</ul>

Template engine memberi sintaks ekspresif dan — yang terpenting — {{ }} otomatis meng-escape output. Nilai $u->nama yang berisi <script>alert(1)</script> akan dirender sebagai teks polos, bukan dieksekusi. Ini pertahanan XSS baris pertama (lengkap di episode 17).

Blade: Layout, Component, Partial

Layout dengan Template Inheritance

Blade menggunakan @extends dan @section untuk mewarisi layout:

resources/views/layouts/app.blade.php
<!DOCTYPE html>
<html lang="id">
<head>
    <title>@yield("title") - Toko</title>
</head>
<body>
    <header>@include("partials.navbar")</header>
 
    <main>
        @yield("content")
    </main>
 
    <footer{{ date("Y") }} Devvnull</footer>
</body>
</html>
resources/views/pages/beranda.blade.php
@extends("layouts.app")
 
@section("title", "Beranda")
 
@section("content")
    <h1>Selamat datang, {{ $nama }}!</h1>
    <p>Kami punya {{ $jumlahProduk }} produk tersedia.</p>
@endsection

Kontroller tinggal mengembalikan view:

Controller mengembalikan view
public function beranda()
{
    return view("pages.beranda", [
        "nama" => "Devvnull",
        "jumlahProduk" => 42,
    ]);
}

Partial dan Component

  • Partial (@include("partials.navbar")) — potongan yang di-include dengan data.
  • Component — blok UI reusable dengan kelas/logika sendiri:
resources/views/components/alert.blade.php
@props(["type" => "info"])
 
<div class="alert alert-{{ $type }}" role="alert">
    {{ $slot }}
</div>
Pemakaian component
<x-alert type="warning">
    Stok produk akan segera habis!
</x-alert>

Component adalah evolusi modern dari partial: lebih terstruktur, punya atribut props, dan mudah diuji.

Directives Penting

Directive penting di Blade
{{ $nama }}                          {{-- escaped output --}}
{!! $htmlAman !!}                    {{-- tanpa escape: HATI-HATI --}}
 
@if ($stok > 0)
    <p>Masih ada {{ $stok }}</p>
@else
    <p>Stok habis</p>
@endif
 
@foreach ($produk as $p)
    <li>{{ $p->nama }}</li>
@endforeach
 
{{-- @forelse, @switch, @auth/@guest, @error, dll --}}

Aturan emas: hampir selalu pakai {{ }}. {!! !!} menonaktifkan escaping — hanya untuk HTML yang sudah dipastikan aman (misal dari CMS terpercaya yang di-sanitize), dan itu pun harus dikaji ulang.

Twig: Sintaks dan Autoescape

Twig (Symfony) menggunakan sintaks berbeda tapi konsep sama:

templates/produk/list.html.twig
{% extends "base.html.twig" %}
 
{% block body %}
    <h1>Daftar Produk</h1>
 
    <table>
        <tr><th>Nama</th><th>Harga</th></tr>
        {% for p in produk %}
            <tr>
                <td>{{ p.nama }}</td>
                <td>{{ p.harga|number_format(0, ",", ".") }}</td>
            </tr>
        {% else %}
            <tr><td colspan="2">Belum ada produk.</td></tr>
        {% endfor %}
    </table>
{% endblock %}

Twig sangat menekankan keamanan dan keterbatasan: tidak ada cara langsung memanggil fungsi PHP sewenang-wenang (kecuali diizinkan via extension). Filter (|number_format, |upper, |date) adalah cara transformasi data. Twig juga autoescape secara default — sama seperti Blade.

Blade vs Twig

AspekBlade (Laravel)Twig (Symfony)
Sintaks@if, {{ }}{% if %}, {{ }}
FilosofiKompilasi ke PHP, ekspresifIsolasi kuat, terbatas
ComponentKelas + Blade, sangat matangTwig components (lebih baru)
AutoescapeYa ({{ }})Ya (default, ketat)
Cocok untukAplikasi LaravelSymfony, project non-Laravel

Tip

Konsep inti identik: layout inheritance, partial, component, dan escaping otomatis. Kalian yang menguasai Blade akan paham Twig dalam hitungan jam dan sebaliknya. Fokus pada prinsip, bukan sintaksnya.

Melindungi Output: XSS dan Escaping

Ini inti keamanan templating. Aturan tiga lapis:

  1. Escape di output{{ }}/autoescape mengubah <script> menjadi &lt;script&gt; sehingga tidak dieksekusi browser.
  2. Jangan pernah menaruh HTML mentah dari user — kecuali setelah sanitasi ketat (misal HTMLPurifier), dan tetap hindari bila bisa.
  3. Escape sesuai konteks — untuk atribut, URL, dan JavaScript gunakan escaping yang sesuai konteks (Blade/Twig menangani HTML; untuk JS embed, minimal JSON-encode).
Contoh konteks escaping
{{ $user->nama }}                        {{-- teks --}}
{{ $user->nama }}                        {{-- di dalam atribut: tetap safe di Blade --}}
<img src="{{ $url }}" alt="{{ $nama }}">
 
{{-- data untuk JavaScript: json_encode dulu --}}
<script>
const user = @json($user);               {{-- Laravel --}}
</script>

Danger

{!! !!} (Blade) dan |raw (Twig) menonaktifkan escaping. Memakainya untuk data pengguna sama dengan mengundang XSS. Batasi penggunaannya hanya untuk konten yang kalian kendalikan penuh dan sudah disanitasi.

Common Pitfalls

  • {!! $input !!} tanpa sanitasi — XSS store; escape selalu di default.
  • Logika bisnis di view — pindahkan ke controller/service; view hanya menampilkan.
  • Query di dalam view — contoh $user->orders memicu N+1 (episode 18); eager-load di controller.
  • Meng-cache view — pastikan php artisan view:cache tidak meninggalkan versi lama saat deploy (episode 19).

Penutup

Inti yang harus dibawa pulang:

  • Template engine memisahkan logika dan presentasi, serta meng-escape output secara otomatis.
  • Blade: @extends/@section untuk layout, @include untuk partial, <x-component> untuk component.
  • Twig: {% extends %}/{% block %}, filter |..., autoescape ketat.
  • Selalu pakai {{ }}{!! !!}/|raw hanya untuk HTML yang dipastikan aman.

Di episode 16 selanjutnya kita melihat ke dalam bahasa itu sendiri: PHP 8.5 & Fitur Terbaru — pipe operator |>, extension URI, Clone With, #[NoDiscard], array_first()/array_last(), penyempurnaan CLI, plus rekap fitur 8.3 dan 8.4 seperti property hooks dan typed class constants, hingga arah PHP 8.6.

Belajar PHP - Templating & Views | Belajar PHP