CodeIgniter4: Layouting View dan Kustom Halaman 404 pada Project Portofolio

6 minute read

Published:

Pengelolaan halaman yang efisien merupakan aspek penting dalam pengembangan aplikasi web, khususnya untuk menjaga struktur tampilan yang terorganisir dan memudahkan pengelolaan di masa mendatang. Pada artikel ini kita mempraktikkan layouting halaman menggunakan studi kasus web statis portofolio dan kustomisasi 404.

View Portfolio

Template layout view yang digunakan yaitu iPortfolio dari BootstrapMade. Fokus utamanya adalah memecah view menjadi bagian reusable (template, header, footer) dan merender konten utama pada file v_portfolio.Pendekatan ini membuat halaman lebih terstruktur, mudah dirawat, dan mudah dikembangkan menjadi multi halaman. Sebagai pelengkap, halaman 404 Page Not Found juga dikustom agar pengalaman pengguna tetap konsisten dengan identitas tampilan portofolio.

1. Persiapan Aset Template iPortfolio

Mulai dari project CI4 yang sudah berjalan, lalu siapkan aset template iPortfolio pada direktori public.

Langkah umum:

  • Download template iPortfolio dari BootstrapMade.
  • Copy folder assets dan paste di public/.
  • Pastikan file utama seperti CSS dan JS dapat diakses dari browser.

Contoh pemanggilan aset CSS pada HTML:

<link href="<?= base_url('assets/css/main.css'); ?>" rel="stylesheet">

Contoh pemanggilan aset JS pada HTML:

<script src="<?= base_url('assets/js/main.js'); ?>"></script>

2. Struktur Layout di Folder Views

Buat struktur view berikut agar layout modular:

app/Views/
  v_portfolio.php
  layouts/
    template.php
    header.php
    footer.php

Isi app/Views/layouts/template.php dengan kode berikut:

<?= $this->include('layouts/header'); ?>
<?= $this->renderSection('content'); ?>
<?= $this->include('layouts/footer'); ?>

Tiga file tersebut memiliki peran:

  • header.php: tag awal HTML, meta, CSS, sidebar/menu.
  • template.php: kerangka untuk menyisipkan section konten.
  • footer.php: penutup halaman dan script JS.

Contoh kode header pada app/Views/layouts/header.php:

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="utf-8">
  <meta content="width=device-width, initial-scale=1.0" name="viewport">
  <title><?= esc($title ?? 'Portfolio') ?></title>
  <meta content="" name="description">
  <meta content="" name="keywords">

  <!-- Favicons -->
  <link href="<?= base_url('assets/img/favicon.png'); ?>" rel="icon">
  <link href="<?= base_url('assets/img/apple-touch-icon.png'); ?>" rel="apple-touch-icon">

  <!-- Fonts -->
  <link href="https://fonts.googleapis.com" rel="preconnect">
  <link href="https://fonts.gstatic.com" rel="preconnect" crossorigin>
  <link href="https://fonts.googleapis.com/css2?family=Roboto:ital,wght@0,100;0,300;0,400;0,500;0,700;0,900;1,100;1,300;1,400;1,500;1,700;1,900&family=Poppins:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&family=Raleway:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&display=swap" rel="stylesheet">

  <!-- Vendor CSS Files -->
  <link href="<?= base_url('assets/vendor/bootstrap/css/bootstrap.min.css'); ?>" rel="stylesheet">
  <link href="<?= base_url('assets/vendor/bootstrap-icons/bootstrap-icons.css'); ?>" rel="stylesheet">
  <link href="<?= base_url('assets/vendor/aos/aos.css'); ?>" rel="stylesheet">
  <link href="<?= base_url('assets/vendor/glightbox/css/glightbox.min.css'); ?>" rel="stylesheet">
  <link href="<?= base_url('assets/vendor/swiper/swiper-bundle.min.css'); ?>" rel="stylesheet">

  <!-- Main CSS File -->
  <link href="<?= base_url('assets/css/main.css'); ?>" rel="stylesheet">

  <!-- =======================================================
  * Template Source: https://bootstrapmade.com/iportfolio-bootstrap-portfolio-websites-template/
  ======================================================== -->
</head>

<body class="index-page">

  <header id="header" class="header dark-background d-flex flex-column">
    <i class="header-toggle d-xl-none bi bi-list"></i>

    <div class="profile-img">
      <img src="<?= base_url('assets/img/my-profile-img.jpg'); ?>" alt="" class="img-fluid rounded-circle">
    </div>

    <a href="<?= base_url('/portfolio'); ?>" class="logo d-flex align-items-center justify-content-center">
      <h1 class="sitename"><?= $name; ?></h1>
    </a>

    <nav id="navmenu" class="navmenu">
      <ul>
        <li><a href="#hero" class="active"><i class="bi bi-house navicon"></i>Home</a></li>
        <li><a href="#about"><i class="bi bi-person navicon"></i> About</a></li>
        <li><a href="#resume"><i class="bi bi-file-earmark-text navicon"></i> Resume</a></li>
        <li><a href="#portfolio"><i class="bi bi-images navicon"></i> Portfolio</a></li>
        <li><a href="#services"><i class="bi bi-hdd-stack navicon"></i> Services</a></li>
        <li><a href="#contact"><i class="bi bi-envelope navicon"></i> Contact</a></li>
      </ul>
    </nav>

  </header>

Contoh kode footer pada app/Views/layouts/footer.php:

  <footer id="footer" class="footer position-relative light-background">

    <div class="container">
      <div class="copyright text-center ">
        <p>&copy; <?= date('Y'); ?> Portofolio <?= $name; ?></p>
      </div>
    </div>

  </footer>

  <!-- Scroll Top -->
  <a href="#" id="scroll-top" class="scroll-top d-flex align-items-center justify-content-center"><i class="bi bi-arrow-up-short"></i></a>

  <!-- Preloader -->
  <div id="preloader"></div>

  <!-- Vendor JS Files -->
  <script src="<?= base_url('assets/vendor/bootstrap/js/bootstrap.bundle.min.js'); ?>"></script>
  <script src="<?= base_url('assets/vendor/php-email-form/validate.js'); ?>"></script>
  <script src="<?= base_url('assets/vendor/aos/aos.js'); ?>"></script>
  <script src="<?= base_url('assets/vendor/typed.js/typed.umd.js'); ?>"></script>
  <script src="<?= base_url('assets/vendor/purecounter/purecounter_vanilla.js'); ?>"></script>
  <script src="<?= base_url('assets/vendor/waypoints/noframework.waypoints.js'); ?>"></script>
  <script src="<?= base_url('assets/vendor/glightbox/js/glightbox.min.js'); ?>"></script>
  <script src="<?= base_url('assets/vendor/imagesloaded/imagesloaded.pkgd.min.js'); ?>"></script>
  <script src="<?= base_url('assets/vendor/isotope-layout/isotope.pkgd.min.js'); ?>"></script>
  <script src="<?= base_url('assets/vendor/swiper/swiper-bundle.min.js'); ?>"></script>

  <!-- Main JS File -->
  <script src="<?= base_url('assets/js/main.js'); ?>"></script>

</body>

</html>

4. Membuat View Utama v_portfolio

Sekarang isi app/Views/v_portfolio.php menggunakan pola extend-section.

<?= $this->extend('layouts/template'); ?>

<?= $this->section('content'); ?>
  <main class="main">

    <!-- Hero Section -->
    <section id="hero" class="hero section dark-background">
      <img src="<?= base_url('assets/img/hero-bg.jpg'); ?>" alt="" data-aos="fade-in" class="">
      <div class="container" data-aos="fade-up" data-aos-delay="100">
        <h2><?= $name; ?></h2>
        <p>I'm <span class="typed" data-typed-items="Designer, Developer, Freelancer, Photographer">Designer</span><span class="typed-cursor typed-cursor--blink" aria-hidden="true"></span><span class="typed-cursor typed-cursor--blink" aria-hidden="true"></span></p>
      </div>
    </section>
    <!-- /Hero Section -->

    <!-- About Section -->
    <section id="about" class="about section">
      <!-- Section Title -->
      <div class="container section-title" data-aos="fade-up">
        <h2>About</h2>
        <p>Magnam dolores commodi suscipit. </p>
      </div>
      <!-- End Section Title -->
      <div class="container" data-aos="fade-up" data-aos-delay="100">
        <div class="row gy-4 justify-content-center">
          <div class="col-lg-4">
            <img src="<?= base_url('assets/img/my-profile-img.jpg'); ?>" class="img-fluid" alt="">
          </div>
          <div class="col-lg-8 content">
            <h2>UI/UX Designer &amp; Web Developer.</h2>
            <p class="fst-italic py-3">
              Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore
              magna aliqua.
            </p>
            <div class="row">
              <div class="col-lg-6">
                <ul>
                  <li><i class="bi bi-chevron-right"></i> <strong>Birthday:</strong> <span>1 May 1995</span></li>
                  <li><i class="bi bi-chevron-right"></i> <strong>Website:</strong> <span>www.example.com</span></li>
                  <li><i class="bi bi-chevron-right"></i> <strong>Phone:</strong> <span>+123 456 7890</span></li>
                  <li><i class="bi bi-chevron-right"></i> <strong>City:</strong> <span>Jakarta, Indonesia</span></li>
                </ul>
              </div>
              <div class="col-lg-6">
                <ul>
                  <li><i class="bi bi-chevron-right"></i> <strong>Age:</strong> <span>30</span></li>
                  <li><i class="bi bi-chevron-right"></i> <strong>Degree:</strong> <span>Master</span></li>
                  <li><i class="bi bi-chevron-right"></i> <strong>Email:</strong> <span>email@example.com</span></li>
                  <li><i class="bi bi-chevron-right"></i> <strong>Freelance:</strong> <span>Available</span></li>
                </ul>
              </div>
            </div>
            <p class="py-3">
              Officiis eligendi itaque labore et dolorum mollitia officiis optio vero. 
            </p>
          </div>
        </div>
      </div>
    </section>
    <!-- /About Section -->
<?= $this->endSection(); ?>

Catatan:

  • ID section seperti hero, about, resume, portfolio, contact selaras dengan struktur iPortfolio.
  • Anda tidak wajib memindahkan seluruh isi index.html sekaligus. Mulai dari section utama, lalu lanjut bertahap.

5. Controller dan Route untuk Halaman Portfolio

Contoh sederhana pada app/Controllers/Home.php:

public function index()
{
  return view('v_portfolio', [
    'title' => 'Portfolio CI4',
    'name' => 'Ircham Ali',
  ]);
}

Tambahkan route pada app/Config/Routes.php:

$routes->get('/', 'Home::index');
$routes->get('/portfolio', 'Home::index');

6. Kustomisasi Halaman 404

Secara default, tampilan 404 berbeda antara mode development dan production. Agar seragam dengan tema portofolio, buat halaman kustom di:

app/Views/errors/not_found.php

Contoh isi sederhana:

<!doctype html>
<html lang="id">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>404 - Halaman Tidak Ditemukan</title>
  <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body class="bg-light">
  <div class="container py-5">
    <div class="card shadow mx-auto" style="max-width: 36rem;">
      <h3 class="card-header text-center display-1 text-muted">404</h3>
      <div class="card-body text-center">
        <p class="text-muted mb-4">Maaf, halaman yang Anda cari tidak tersedia.</p>
        <a href="<?= base_url('/portfolio'); ?>" class="btn btn-dark">Kembali ke Portofolio</a>
      </div>
    </div>
  </div>
</body>
</html>

Aktifkan override di app/Config/Routes.php:

$routes->set404Override(function () {
  echo view('errors/not_found');
});

View 404

7. Checklist Pengujian

Lakukan pengecekan akhir berikut:

  • Halaman /portfolio menampilkan struktur section utama iPortfolio.
  • Aset CSS/JS termuat tanpa error path.
  • Navigasi menu bergerak ke section yang tepat.
  • URL acak (misalnya /halaman-tidak-ada) menampilkan halaman 404 custom.

8. Ringkasan

Praktik ini menunjukkan bahwa layouting di CI4 bisa langsung diintegrasikan dengan template statis populer seperti iPortfolio melalui pendekatan modular.

Poin utama:

  • File v_portfolio memegang konten utama halaman.
  • Folder layouts berisi tiga file penting: template.php, header.php, dan footer.php.
  • Halaman 404 dapat disesuaikan agar satu gaya dengan branding portfolio.

Dengan pola ini, Anda bisa melanjutkan ke tahap berikutnya: memecah section menjadi partial yang lebih kecil, menambahkan data dinamis dari database, dan membuat halaman detail project per portfolio item.