Membangun sistem templating yang rapi di CodeIgniter 4: me-render view dengan data dari controller, layout reusable dengan section dan partial, view cells untuk komponen dinamis, serta kebiasaan escape output.

Setelah di episode 4 kita menguasai routing dan controllers, pada episode ini kita fokus ke sisi "V" dari MVC: Views dan templating. Di sinilah data dari controller diubah menjadi HTML yang kalian lihat di browser.
Mengapa templating penting? Karena aplikasi nyata tidak punya satu halaman — punya puluhan bahkan ratusan. Tanpa sistem layout yang reusable, kalian akan mengulang <head>, navbar, dan footer di setiap file. CodeIgniter 4 menyediakan view layouts, section, partial, dan view cells untuk menghindari pengulangan ini.
Controller me-render view dengan method view() dan mengoper data sebagai array:
<?php
namespace App\Controllers;
class Posts extends BaseController
{
public function index(): string
{
$data = [
'title' => 'Daftar Post',
'posts' => [
['id' => 1, 'title' => 'Hello World', 'status' => 'published'],
['id' => 2, 'title' => 'Belajar MVC', 'status' => 'draft'],
],
];
return view('posts/index', $data);
}
}Di dalam view, array data di-ekstrak menjadi variabel:
<h1><?= esc($title) ?></h1>
<?php foreach ($posts as $post): ?>
<article>
<h2><?= esc($post['title']) ?></h2>
<span><?= esc($post['status']) ?></span>
</article>
<?php endforeach; ?>esc() adalah HTML escape yang mencegah XSS — kebiasaan yang wajib kalian tanamkan sejak episode ini. Tidak ada data dari pengguna yang boleh dirender tanpa esc() (detail di episode 8).
Tip
view() menerima path relatif ke app/Views/. Subfolder didukung: view('posts/index') membaca app/Views/posts/index.php. Struktur folder view yang rapi mengikuti struktur controller.
CodeIgniter 4 mendukung view layouts dengan $this->extend() dan $this->section(). Buat layout induk:
<!DOCTYPE html>
<html lang="id">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title><?= esc($title ?? 'CodeIgniter') ?></title>
</head>
<body>
<header>
<nav>
<a href="<?= site_url('/') ?>">Home</a>
<a href="<?= site_url('posts') ?>">Posts</a>
</nav>
</header>
<main>
<?= $this->renderSection('content') ?>
</main>
<footer>
<p>© <?= date('Y') ?> DevNull</p>
</footer>
</body>
</html>$this->renderSection('content') adalah placeholder yang diisi oleh view anak. Sekarang view halaman:
<?= $this->extend('layouts/default') ?>
<?= $this->section('content') ?>
<h1><?= esc($title) ?></h1>
<?php foreach ($posts as $post): ?>
<article>
<h2><?= esc($post['title']) ?></h2>
</article>
<?php endforeach; ?>
<?= $this->endSection() ?>View anak hanya berisi bagian yang berbeda — semua kerangka (head, nav, footer) diambil dari layout. Ini menghilangkan duplikasi di seluruh aplikasi.
Layout bisa punya lebih dari satu section. Misalnya sidebar:
<aside>
<?= $this->renderSection('sidebar') ?>
</aside><?= $this->section('sidebar') ?>
<ul>
<li>Kategori</li>
<li>Arsip</li>
</ul>
<?= $this->endSection() ?>Bagian kecil yang berulang — navbar, card, form field — bisa di-render sebagai partial:
<?= $this->include('partials/_post_card', ['post' => $post]) ?><article class="card">
<h3><?= esc($post['title']) ?></h3>
<a href="<?= site_url('posts/' . $post['id']) ?>">Baca selengkapnya</a>
</article>Untuk komponen yang butuh data sendiri (dari database, misalnya), gunakan view cells — semacam "komponen mandiri":
<?= view_cell('App\Views\Cells\LatestPosts::show') ?><?php
namespace App\Views\Cells;
class LatestPosts
{
public function show(): string
{
$posts = [
['id' => 1, 'title' => 'Post terbaru 1'],
['id' => 2, 'title' => 'Post terbaru 2'],
];
return view('cells/latest_posts', ['posts' => $posts]);
}
}View cell bisa menerima parameter (view_cell('...::show', ['limit' => 5])) dan cocok untuk widget yang muncul di banyak halaman.
Warning
Jangan jalankan query database langsung di view. Semua pengambilan data idealnya di controller atau model — kecuali komponen mandiri seperti view cell yang memang dirancang untuk mengelola datanya sendiri.
esc($data, $context) memproteksi output sesuai konteks:
<?= esc($title) ?> // konteks HTML
<?= esc($attr, 'attr') ?> // konteks atribut HTML
<?= esc($javascript, 'js') ?> // konteks JavaScriptGunakan selalu di view. Aturan sederhananya: apapun yang berasal dari user atau database, anggap tidak aman sampai di-escape.
Jika kalian tidak ingin menulis PHP murni di view, CodeIgniter menyediakan Template Parser dengan sintaks {...}:
$parser = \Config\Services::parser();
$data = ['title' => 'Halo', 'posts' => $posts];
return $parser->setData($data)
->render('posts/index');<h1>{title}</h1>
{posts}
<p>{title}</p>
{/posts}Parser cocok untuk view yang dikelola non-developer. Namun untuk control flow kompleks, PHP murni di view (dengan layout) jauh lebih fleksibel — inilah yang kita pakai sepanjang series.
view() me-render file di app/Views/ dengan data dari controller.$this->extend(), $this->section(), $this->renderSection().$this->include()) untuk bagian berulang; view cell untuk widget mandiri.esc() di semua output; Parser class untuk sintaks {...} jika dibutuhkan.Inti yang harus dibawa pulang:
esc() — kebiasaan yang mencegah XSS.Di episode 6 selanjutnya kita akan masuk ke sisi "M" dari MVC: model dan database — konfigurasi koneksi database, Query Builder, class Model dengan CRUD dan validation bawaan, serta praktik CRUD posts. Siapkan MySQL kalian, karena mulai episode ini data benar-benar tersimpan!