Project: Blog Sederhana dengan PHP Native (Bagian 1)

Setelah membuat toko online mini, sekarang kita naik satu level: membangun blog sederhana dengan PHP Native. Project ini realistis karena menggabungkan banyak skill penting sekaligus:

  • koneksi database
  • relasi kategori dan artikel
  • halaman admin
  • slug URL
  • status draft vs published
  • pagination di halaman publik

Di bagian pertama ini, kita fokus menyiapkan database, struktur folder, helper, dan dashboard admin dasar.

1. Gambaran Fitur Project

Target akhir blog yang akan kita bangun:

  1. Admin bisa membuat kategori.
  2. Admin bisa menulis artikel.
  3. Artikel bisa disimpan sebagai draft atau published.
  4. Halaman publik menampilkan daftar artikel dengan pagination.
  5. Setiap artikel punya URL detail berbasis slug.

2. Membuat Database

Buat database baru:

CREATE DATABASE blog_sederhana;
USE blog_sederhana;

Lalu buat tabel kategori dan artikel:

CREATE TABLE kategori (
    id INT AUTO_INCREMENT PRIMARY KEY,
    nama_kategori VARCHAR(100) NOT NULL,
    slug VARCHAR(120) NOT NULL UNIQUE,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

CREATE TABLE artikel (
    id INT AUTO_INCREMENT PRIMARY KEY,
    kategori_id INT NOT NULL,
    judul VARCHAR(180) NOT NULL,
    slug VARCHAR(200) NOT NULL UNIQUE,
    ringkasan TEXT NOT NULL,
    isi LONGTEXT NOT NULL,
    thumbnail VARCHAR(255) DEFAULT NULL,
    status ENUM('draft', 'published') DEFAULT 'draft',
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    FOREIGN KEY (kategori_id) REFERENCES kategori(id) ON DELETE RESTRICT
);

Masukkan data awal:

INSERT INTO kategori (nama_kategori, slug) VALUES
('PHP Dasar', 'php-dasar'),
('Project', 'project'),
('Tips Belajar', 'tips-belajar');

INSERT INTO artikel (kategori_id, judul, slug, ringkasan, isi, status) VALUES
(1, 'Cara Belajar PHP untuk Pemula', 'cara-belajar-php-untuk-pemula', 'Langkah bertahap untuk mulai belajar PHP tanpa bingung.', '<p>Isi artikel awal...</p>', 'published'),
(2, 'Membangun CRUD Artikel Pertama', 'membangun-crud-artikel-pertama', 'Struktur sederhana untuk latihan CRUD.', '<p>Isi artikel project...</p>', 'draft');

3. Struktur Folder Project

Buat folder baru blog-sederhana dengan struktur seperti ini:

blog-sederhana/
├── admin/
│   ├── index.php
│   ├── kategori.php
│   ├── artikel-form.php
│   └── proses-artikel.php
├── assets/
│   └── style.css
├── config/
│   └── database.php
├── uploads/
├── functions.php
└── public/
    ├── index.php
    └── detail.php

Penjelasan singkat:

  • admin/ untuk area kelola konten
  • public/ untuk halaman yang dibuka pengunjung
  • config/ untuk koneksi database
  • functions.php untuk helper bersama
  • uploads/ untuk thumbnail artikel

4. Membuat Koneksi Database

Isi file config/database.php:

<?php
$host = '127.0.0.1';
$db   = 'blog_sederhana';
$user = 'root';
$pass = '';

try {
    $pdo = new PDO("mysql:host={$host};dbname={$db};charset=utf8mb4", $user, $pass);
    $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
    $pdo->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
} catch (PDOException $e) {
    die('Koneksi database gagal: ' . $e->getMessage());
}

5. Membuat Helper Dasar

Buat file functions.php:

<?php
function slugify(string $text): string
{
    $text = strtolower(trim($text));
    $text = preg_replace('/[^a-z0-9]+/i', '-', $text);
    return trim($text, '-');
}

function excerpt(string $html, int $limit = 140): string
{
    $text = strip_tags($html);

    if (strlen($text) <= $limit) {
        return $text;
    }

    return substr($text, 0, $limit) . '...';
}

function badgeStatus(string $status): string
{
    return $status === 'published' ? '✅ Published' : '📝 Draft';
}

Helper ini akan sering dipakai:

  • slugify() untuk URL rapi seperti /detail.php?slug=belajar-php
  • excerpt() untuk ringkasan di daftar artikel
  • badgeStatus() untuk tampilan status di dashboard admin

6. Membuat Dashboard Admin Dasar

Sekarang buat file admin/index.php:

<?php
require __DIR__ . '/../config/database.php';
require __DIR__ . '/../functions.php';

$stmt = $pdo->query("
    SELECT artikel.*, kategori.nama_kategori
    FROM artikel
    INNER JOIN kategori ON kategori.id = artikel.kategori_id
    ORDER BY artikel.created_at DESC
");
$artikels = $stmt->fetchAll();
?>

<!DOCTYPE html>
<html lang="id">
<head>
    <meta charset="UTF-8">
    <title>Admin Blog</title>
    <link rel="stylesheet" href="../assets/style.css">
</head>
<body>
    <div class="container">
        <div class="page-header">
            <div>
                <h1>Dashboard Blog</h1>
                <p>Kelola artikel dan kategori dari satu tempat.</p>
            </div>
            <div class="actions">
                <a class="btn secondary" href="kategori.php">Kelola Kategori</a>
                <a class="btn" href="artikel-form.php">+ Artikel Baru</a>
            </div>
        </div>

        <table>
            <thead>
                <tr>
                    <th>Judul</th>
                    <th>Kategori</th>
                    <th>Status</th>
                    <th>Dibuat</th>
                </tr>
            </thead>
            <tbody>
                <?php foreach ($artikels as $artikel): ?>
                    <tr>
                        <td><?= htmlspecialchars($artikel['judul']) ?></td>
                        <td><?= htmlspecialchars($artikel['nama_kategori']) ?></td>
                        <td><?= badgeStatus($artikel['status']) ?></td>
                        <td><?= htmlspecialchars($artikel['created_at']) ?></td>
                    </tr>
                <?php endforeach; ?>
            </tbody>
        </table>
    </div>
</body>
</html>

7. Styling Dasar

Isi file assets/style.css dengan style sederhana:

* {
    box-sizing: border-box;
}

body {
    margin: 0;
    font-family: "Segoe UI", Tahoma, sans-serif;
    background: #f5f7fb;
    color: #1f2937;
}

.container {
    max-width: 980px;
    margin: 40px auto;
    padding: 0 16px;
}

.page-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 16px;
    margin-bottom: 24px;
}

.actions {
    display: flex;
    gap: 12px;
}

.btn {
    display: inline-block;
    padding: 10px 16px;
    border-radius: 8px;
    background: #2563eb;
    color: white;
    text-decoration: none;
}

.btn.secondary {
    background: #475569;
}

table {
    width: 100%;
    border-collapse: collapse;
    background: white;
    border-radius: 12px;
    overflow: hidden;
}

th,
td {
    padding: 14px 16px;
    border-bottom: 1px solid #e5e7eb;
    text-align: left;
}

th {
    background: #0f172a;
    color: white;
}

Checklist Setelah Bagian 1

Kalau langkah di atas berhasil, kamu sekarang sudah punya:

  • database blog
  • tabel kategori dan artikel
  • dashboard admin dasar
  • helper untuk slug dan excerpt
  • struktur folder yang siap dikembangkan

Error Umum

SQLSTATE saat membuka dashboard admin

Biasanya nama database, tabel, atau kolom tidak sama dengan query yang dipakai.

Halaman putih kosong

Sering terjadi karena file database.php atau functions.php tidak berhasil di-require. Cek path file dengan teliti.

Unknown column 'nama_kategori'

Kolom itu berasal dari hasil JOIN. Pastikan query memakai:

INNER JOIN kategori ON kategori.id = artikel.kategori_id

Selanjutnya

Fondasi project sudah siap. Di bagian berikutnya, kita akan membuat:

  • CRUD kategori
  • form artikel
  • simpan draft vs published
  • editor WYSIWYG untuk isi artikel

Lanjut ke Blog Sederhana Bagian 2 →