Belajar Symfony - Templating dengan Twig
Episode 5 of 27

Belajar Symfony - Templating dengan Twig

Menguasai Twig sebagai template engine: syntax output & statement, template inheritance dengan block/extends, filter & function bawaan, partials dan macros, objek global app, serta perkenalan UX Twig Components untuk membangun UI yang reusable.

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

Pendahuluan

Setelah di episode 4 controller mengembalikan response JSON, episode ini melengkapi sisi view dari MVC: Twig, template engine resmi Symfony. Di sinilah kalian mengubah data dari controller menjadi HTML yang manusiawi.

Mengapa Twig, bukan PHP template biasa? Twig didesain sebagai bahasa yang aman (auto-escaping bawaan, tidak ada eksekusi PHP sewenang-wenang) dan mudah dibaca (syntax yang lebih ringkas). Keamanan auto-escaping ini sangat penting — kita akan lihat kembali perannya dalam mencegah XSS di episode 18.

Syntax Dasar Twig

Twig memakai tiga pembatas:

PembatasFungsi
{{ expr }}Output — cetak hasil ekspresi
{% statement %}Statement — kontrol alur (if, for, set)
{# comment #}Komentar — tidak dirender

Contoh cepat di dalam template:

Syntax dasar Twig
<h1>{{ article.title }}</h1>
 
{% if article.published %}
    <p>Dipublikasikan pada {{ article.createdAt|date('d M Y') }}</p>
{% else %}
    <p>Draft — belum tampil.</p>
{% endif %}
 
{% for tag in article.tags %}
    <span class="tag">{{ tag }}</span>
{% endfor %}

Perhatikan |date('d M Y') — ini filter. Twig memakai pipe untuk transformasi output, mirip pipeline di shell.

Template Inheritance

Masalah klasik website: header, footer, dan navigasi yang sama di setiap halaman. Solusi Twig adalah inheritance — template anak mewarisi layout induk dan mengisi block tertentu:

templates/base.html.twig
<!DOCTYPE html>
<html lang="id">
<head>
    <meta charset="UTF-8">
    <title>{% block title %}My Blog{% endblock %}</title>
</head>
<body>
    <header>{% block header %}Logo & navigasi{% endblock %}</header>
 
    <main>
        {% block body %}{% endblock %}
    </main>
 
    <footer>{% block footer %}&copy; 2026{% endblock %}</footer>
</body>
</html>
templates/article/index.html.twig
{% extends 'base.html.twig' %}
 
{% block title %}Daftar Artikel{% endblock %}
 
{% block body %}
    <h1>Daftar Artikel</h1>
    <ul>
    {% for article in articles %}
        <li><a href="{{ path('app_article_show', {slug: article.slug}) }}">{{ article.title }}</a></li>
    {% endfor %}
    </ul>
{% endblock %}

Inilah pola yang paling sering kalian lihat di project Symfony nyata: satu layout induk di base.html.twig, dan setiap halaman hanya mendefinisikan block yang berbeda. {{ path() }} adalah function bawaan untuk menghasilkan URL dari nama route — perhatikan penggunaan literal {slug: ...} di dalam pemanggilan function.

Filter dan Function Bawaan

Twig menyediakan ratusan filter dan function. Yang paling sering dipakai:

Filter & function umum
{{ name|upper }}                          {# UPPERCASE #}
{{ article.body|striptags|u.truncate(120) }}  {# ringkas teks #}
{{ price|number_format(2, ',', '.') }}    {# 1.234,56 #}
{{ url|e }}                               {# escape HTML #}
{{ path('app_article_show', {slug: s}) }} {# URL dari route #}
{{ asset('build/app.js') }}               {# URL file statis #}
{{ app.user ? app.user.email : 'anonym' }} {# user login #}

app adalah global object yang menyediakan konteks: app.request, app.session, app.user, dan app.flashes. Ini cara template mengakses data di luar variabel yang dikirim controller.

Partials dan Macros

Ketika blok kecil dipakai berulang, gunakan partial (include) atau macro (function template):

templates/partials/article-card.html.twig
<article class="card">
    <h2><a href="{{ path('app_article_show', {slug: article.slug}) }}">{{ article.title }}</a></h2>
    <p>{{ article.excerpt }}</p>
</article>
Menggunakan partial
{% for article in articles %}
    {{ include('partials/article-card.html.twig', {article: article}) }}
{% endfor %}

Macro bekerja seperti function yang bisa dipanggil berkali-kali tanpa menulis ulang markup:

Macro untuk badge
{% macro status_badge(label, ok) %}
    <span class="badge {{ ok ? 'badge-ok' : 'badge-warn' }}">{{ label }}</span>
{% endmacro %}
 
{% import _self as ui %}
{{ ui.status_badge('Aktif', true) }}

Form Rendering dengan Twig

Twig memiliki integrasi form (kita bangun FormType-nya di episode 7). Ada dua level kontrol: cepat dan manual.

Render form otomatis
{{ form_start(form) }}
    {{ form_row(form.title) }}
    {{ form_row(form.body) }}
    <button type="submit">Simpan</button>
{{ form_end(form) }}

form_row() merender label + widget + error sekaligus; form_widget() hanya widget-nya. Untuk penyesuaian mendalam, tema form (form theme) bisa diganti per-form — topik yang kita selami di episode 7.

Debugging Template

Kesalahan paling umum saat memakai Twig:

Debug template & variable
php bin/console debug:twig          # daftar function & filter
php bin/console lint:twig templates # validasi syntax semua template

dump() di dalam template membantu inspeksi variabel:

Dump variabel di template
{{ dump(article) }}

Kombinasi debug:twig dan lint:twig menangkap mayoritas kesalahan sebelum halaman dibuka.

Warning

Jangan pernah memakai |raw pada data yang berasal dari user tanpa alasan kuat. Filter |raw mematikan auto-escaping dan membuka celah XSS. Kalau kalian butuh HTML dari user, sanitasi dulu di sisi server — kita bahas di episode 18.

UX Twig Components (Sekilas)

Untuk project yang lebih modern, paket Symfony UX menghadirkan Twig Components: komponen PHP+template yang bisa dipakai seperti element HTML (<twig:LikeButton post="{post}" />). Ini menjembatani server-rendering dengan interaktivitas frontend, dan menjadi tren full-stack Symfony di 2026 — kita singgung kembali di episode 25.

Penutup

Pada episode 5 ini, kalian telah menguasai dasar templating Twig.

Inti yang harus dibawa pulang:

  • Tiga pembatas: {{ }} output, {% %} statement, {# #} komentar.
  • Inheritance (extends + block) adalah pola utama layout yang reusable.
  • Filter via |, function via path(), asset(), dan global app.
  • Partial (include) untuk blok berulang, macro untuk logika tampilan.
  • Auto-escaping bawaan Twig — jangan di-mati-kan dengan |raw tanpa alasan.

Di episode 6 selanjutnya kita masuk ke lapisan model: Doctrine ORM & database — membuat entity, menggenerasi dan menjalankan migrasi, relasi antar entity, repository, serta data fixtures untuk mengisi data awal. Sampai jumpa di episode 6!

Belajar Symfony - Templating dengan Twig | Belajar Symfony