Ini adalah website personal Selamat M. Harjono

Kata-Kata Hari Ini

"Baraja ka nan manang, mancontoh ka nan sudah"

210
Des 17
Bootcamp HMMI dengan Tema "Personal Blog & Portfolio Management System" - Fase I

Bootcamp HMMI dengan Tema "Personal Blog & Portfolio Management System" - Fase I

Rabu, 17 Desember 2025 | oleh Selamat Muliyadi Harjono | Materi


FASE 1 – FUNDAMENTAL & DATABASE THINKING (Pertemuan 1–5)

πŸ”Ή CATATAN PEDAGOGIS

  • "Database-First Approach": Peserta memahami struktur data sebelum framework
  • "Why Before How": Setiap konsep diawali dengan analogi kehidupan nyata
  • "Hands-on Every Session": Setiap pertemuan wajib ada praktikum minimal 30%
  • "Project-Driven": Semua materi mengarah ke sistem blog/portfolio final

πŸ“š PERTEMUAN 1 – ORIENTASI & ARSITEKTUR WEB

Durasi: 3 jam (Teori: 1.5 jam, Praktik: 1.5 jam)

🎯 TUJUAN PEMBELAJARAN

  1. Memahami ekosistem pengembangan web modern
  2. Membedakan website statis vs dinamis
  3. Setup environment development siap pakai

πŸ“– MATERI DETAIL

A. Opening & Ice Breaking (15 menit)

  • Perkenalan instruktur & peserta
  • Ice breaker: "Ceritakan blog/portfolio impianmu"
  • Sharing pengalaman dengan CMS (WordPress, dll)


B. Tujuan Bootcamp & Output Akhir (20 menit)

  • Output Final: Sistem Blog & Portfolio dengan fitur:
    • Manajemen artikel (CRUD)
    • Kategori & tag
    • Portfolio projects
    • Authentication (login/register)
    • Admin panel dengan FilamentPHP
    • Responsive design
  • Roadmap 4 Fase:
    • Fase 1: Database Thinking (Pertemuan 1-5)
    • Fase 2: Laravel Fundamental (Pertemuan 6-10)
    • Fase 3: FilamentPHP & Advanced Features (Pertemuan 11-15)
    • Fase 4: Deployment & Final Project (Pertemuan 16-20)


C. Website Statis vs Dinamis (25 menit)

  • Analogi: Brosur vs Toko Online
  • Statis: HTML, CSS, JS saja
    • Contoh: Landing page perusahaan
    • Kelebihan: Cepat, murah hosting
    • Kekurangan: Konten tidak terupdate
  • Dinamis: PHP + Database
    • Contoh: Blog, E-commerce
    • Kelebihan: Interaktif, mudah update
    • Kekurangan: Butuh server processing


D. Backend, Database, dan CMS (30 menit)

  • Three-Tier Architecture:
  • Frontend (Tampilan) ←→ Backend (Logika) ←→ Database (Penyimpanan)

  • CMS sebagai contoh: WordPress = PHP + MySQL + Theme System
  • Our Stack:
    • PHP 8.x (Bahasa pemrograman)
    • Laravel (Framework)
    • FilamentPHP (Admin Panel)
    • MySQL/SQLite (Database)
    • Herd (Local Server)


E. Contoh Arsitektur Blog Modern (20 menit)

Arsitektur Web Modern

F. Setup Environment (60 menit - PRAKTIKUM)

Langkah-langkah:

  1. Install Herd (Alternatif: Laragon/XAMPP)
    1. bash
    2. # Download dari herd.laravel.com
      # Install dengan default settings
  2. Verifikasi Install:
    1. bash
    2. # Buka terminal
      php --version
      composer --version
  3. Install VS Code & Extension:
    • PHP Intelephense
    • Laravel Extension Pack
    • MySQL/MariaDB
    • Live Server
  4. Install TablePlus:
    • Setup koneksi ke MySQL
    • Test connection
  5. Test Environment:
    1. php
    2. <?php
      // buat file test.php di herd/www
      phpinfo();
      ?>


G. PHP Overview (30 menit)

  • Sejarah singkat: Personal Home Page β†’ PHP: Hypertext Preprocessor
  • Syntax dasar:
    • php
    • <?php
      echo "Hello Bootcamp!";
      ?>
  • PHP dalam web:
    • html
    • <!DOCTYPE html>
      <html>
      <body>
        <h1><?php echo "Judul Dinamis"; ?></h1>
      </body>
      </html>

🎯 TUGAS PERTEMUAN 1

  1. Install semua software (Herd, TablePlus, VS Code)
  2. Buat file profile.php dengan isi:
    • Nama, asal kota, hobi (dalam variabel PHP)
    • Tampilkan dengan echo
  3. Browsing 3 blog/portfolio favorit, analisis fiturnya

πŸ“š PERTEMUAN 2 – PHP FUNDAMENTAL (PRAKTIK-ORIENTED)

Durasi: 3 jam (Teori: 1 jam, Praktik: 2 jam)

🎯 TUJUAN PEMBELAJARAN

  1. Menguasai sintaks PHP dasar
  2. Mampu menangani form HTML dengan PHP
  3. Membuat sistem input artikel sederhana

πŸ“– MATERI DETAIL

A. Review & Q&A (15 menit)

  • Diskusi tugas pertemuan 1
  • Troubleshooting installasi


B. Variabel & Tipe Data (30 menit)

php

<?php
// Deklarasi variabel
$nama = "Budi";           // String
$umur = 25;               // Integer
$tinggi = 175.5;          // Float
$is_mahasiswa = true;     // Boolean
$hobi = ["Membaca", "Coding", "Gaming"]; // Array

// Output
echo "Nama: $nama";       // String interpolation
echo 'Umur: ' . $umur;    // Concatenation
?>


C. Kontrol Alur (45 menit)

Percabangan:

php

<?php
$nilai = 85;

// If-else
if ($nilai >= 90) {
    $grade = "A";
} elseif ($nilai >= 80) {
    $grade = "B";
} else {
    $grade = "C";
}

// Switch-case
switch($grade) {
    case "A":
        $keterangan = "Luar Biasa!";
        break;
    case "B":
        $keterangan = "Baik";
        break;
    default:
        $keterangan = "Perbaikan";
}
?>


Perulangan:

php

<?php
// For loop
for($i = 1; $i <= 5; $i++) {
    echo "Artikel ke-$i<br>";
}

// Foreach
$kategori = ["Teknologi", "Desain", "Bisnis"];
foreach($kategori as $kategori) {
    echo "- $kategori<br>";
}

// While
$counter = 1;
while($counter <= 3) {
    echo "Loading... ($counter/3)<br>";
    $counter++;
}
?>


D. Function & Array (45 menit)

Function:

php

<?php
// Function dasar
function hitungLuas($panjang, $lebar) {
    return $panjang * $lebar;
}

// Function dengan default parameter
function sapaUser($nama, $waktu = "pagi") {
    return "Selamat $waktu, $nama!";
}

// Pemanggilan
echo hitungLuas(10, 5);  // Output: 50
echo sapaUser("Ani");    // Output: Selamat pagi, Ani!
?>


Array Multidimensi:

php

<?php
// Array artikel
$artikel = [
    [
        "judul" => "Belajar PHP",
        "penulis" => "Budi",
        "views" => 1000
    ],
    [
        "judul" => "Database Design",
        "penulis" => "Sari",
        "views" => 2500
    ]
];

// Akses data
echo $artikel[0]["judul"];  // Output: Belajar PHP
?>


E. Form Handling (45 menit - PRAKTIKUM)

File: form_artikel.php:

php

<!DOCTYPE html>
<html>
<head>
    <title>Input Artikel</title>
</head>
<body>
    <h2>Form Input Artikel</h2>
    
    <form method="POST" action="proses_artikel.php">
        <label>Judul:</label><br>
        <input type="text" name="judul" required><br><br>
        
        <label>Konten:</label><br>
        <textarea name="konten" rows="5" required></textarea><br><br>
        
        <label>Kategori:</label><br>
        <select name="kategori">
            <option value="teknologi">Teknologi</option>
            <option value="desain">Desain</option>
            <option value="bisnis">Bisnis</option>
        </select><br><br>
        
        <label>Tags (pisahkan koma):</label><br>
        <input type="text" name="tags" placeholder="PHP, Database, Web"><br><br>
        
        <input type="submit" value="Simpan Artikel">
    </form>
</body>
</html>


File: proses_artikel.php:

php

<?php
// Tangkap data dari form
$judul = $_POST['judul'];
$konten = $_POST['konten'];
$kategori = $_POST['kategori'];
$tags = explode(',', $_POST['tags']); // Convert ke array

// Simpan ke session (sementara)
session_start();
$_SESSION['artikel'] = [
    'judul' => $judul,
    'konten' => $konten,
    'kategori' => $kategori,
    'tags' => $tags,
    'tanggal' => date('Y-m-d H:i:s')
];

// Redirect ke halaman tampil
header('Location: tampil_artikel.php');
?>


F. Session Management (30 menit)

php

<?php
// File: tampil_artikel.php
session_start();

// Cek apakah ada data session
if(!isset($_SESSION['artikel'])) {
    die("Tidak ada artikel untuk ditampilkan");
}

$artikel = $_SESSION['artikel'];
?>
<!DOCTYPE html>
<html>
<body>
    <h1><?php echo htmlspecialchars($artikel['judul']); ?></h1>
    <p><strong>Kategori:</strong> <?php echo $artikel['kategori']; ?></p>
    <p><strong>Tanggal:</strong> <?php echo $artikel['tanggal']; ?></p>
    <hr>
    <p><?php echo nl2br(htmlspecialchars($artikel['konten'])); ?></p>
    
    <h3>Tags:</h3>
    <ul>
    <?php foreach($artikel['tags'] as $tag): ?>
        <li><?php echo trim($tag); ?></li>
    <?php endforeach; ?>
    </ul>
    
    <a href="form_artikel.php">Tambah Artikel Baru</a>
</body>
</html>


G. Studi Kasus: Sistem Artikel Sederhana (60 menit - PRAKTIKUM)

Tugas Kelompok:

  1. Buat sistem dengan 3 halaman:
    • index.php: Daftar artikel (dari session/array)
    • tambah.php: Form input artikel
    • detail.php: Detail artikel
  2. Fitur:
    • Validasi form (judul & konten wajib diisi)
    • Filter HTML special characters
    • Penyimpanan multiple articles
    • Fitur edit/delete sederhana

🎯 TUGAS PERTEMUAN 2

  1. Buat sistem blog mini dengan fitur:
    • Input artikel (judul, konten, kategori, tags)
    • Tampilkan daftar artikel
    • Detail artikel
  2. Implementasikan:
    • Session untuk penyimpanan sementara
    • Form validation
    • Array multidimensi untuk struktur data
  3. Kumpulkan screenshot dan kode di GitHub/GitLab

πŸ“š PERTEMUAN 3 – DATABASE RELASIONAL (KONSEPTUAL & TEKNIS)

Durasi: 3 jam (Teori: 1.5 jam, Praktik: 1.5 jam)

🎯 TUJUAN PEMBELAJARAN

  1. Memahami konsep database relasional
  2. Mampu mendesain ERD untuk sistem blog
  3. Mengidentifikasi entity, attribute, relationship

πŸ“– MATERI DETAIL

A. Review & Problem Statement (30 menit)

  • Diskusi kelemahan sistem session-based
  • Problem: Data hilang saat browser ditutup
  • Problem: Tidak bisa query/filter data
  • Solusi: Butuh database!


B. Konsep Data vs Informasi (30 menit)

  • Data: Fakta mentah (contoh: "25", "Budi", "2024-01-15")
  • Informasi: Data yang terorganisir & bermakna
    • text
    • Data: "25", "Budi", "2024-01-15"
      Informasi: "Budi berusia 25 tahun, lahir tanggal 15 Januari 2024"
  • Database: Kumpulan data terorganisir
  • DBMS: Software untuk manage database (MySQL, PostgreSQL, SQLite)


C. Entity, Attribute, Relationship (45 menit)

1. Entity (Entitas):

  • Objek nyata dalam sistem
  • Contoh untuk blog:
    • User
    • Post (Artikel)
    • Category
    • Tag
    • Comment
    • Portfolio Project

2. Attribute (Atribut):

  • Sifat/karakteristik entity
  • Contoh entity "Post":
    • id (primary key)
    • title
    • content
    • author_id
    • category_id
    • created_at

3. Relationship (Hubungan):

  • Relasi antar entity
  • Contoh:
    • User menulis Post (one-to-many)
    • Post memiliki Category (many-to-one)
    • Post memiliki Tags (many-to-many)


D. Primary Key & Foreign Key (30 menit)

  • Primary Key (PK): Identifier unik dalam tabel
    • sql
    • CREATE TABLE users (
          id INT PRIMARY KEY AUTO_INCREMENT,
          username VARCHAR(50) UNIQUE
      );
  • Foreign Key (FK): Referensi ke PK tabel lain
    • sql
    • CREATE TABLE posts (
          id INT PRIMARY KEY AUTO_INCREMENT,
          user_id INT,
          FOREIGN KEY (user_id) REFERENCES users(id)
      );


E. Cardinality (30 menit)

  • One-to-One (1:1): 1 user memiliki 1 profile
    • text
    • User (1) ──── (1) UserProfile
  • One-to-Many (1:N): 1 user menulis N posts
    • text
    • User (1) ──── (N) Post
  • Many-to-Many (N:M): N posts memiliki M tags
    • text
    • Post (N) ──── (M) Tag


F. ERD untuk Blog System (60 menit - PRAKTIKUM)

Langkah-langkah desain:

  1. Identifikasi Entity:
    1. text
    2. User
      Post
      Category
      Tag
      Comment
      Portfolio
  2. Tentukan Attribute masing-masing:
    1. yaml
    2. User:
        - id (PK)
        - username
        - email
        - password
        - role
        - created_at
      
      Post:
        - id (PK)
        - title
        - slug
        - content
        - excerpt
        - featured_image
        - user_id (FK)
        - category_id (FK)
        - status
        - published_at
        - created_at
        - updated_at
  3. Tentukan Relationship:
    1. text
    2. User β”€β”€βŸ¨writesβŸ©β”€β”€ Post (1:N)
      Post β”€β”€βŸ¨belongs toβŸ©β”€β”€ Category (N:1)
      Post β”€β”€βŸ¨hasβŸ©β”€β”€ Tag (N:M) via post_tag
      Post β”€β”€βŸ¨receivesβŸ©β”€β”€ Comment (1:N)
      User β”€β”€βŸ¨hasβŸ©β”€β”€ Portfolio (1:N)
  4. Buat ERD dengan diagram (gunakan draw.io atau Lucidchart):
    1. text
    2. [USER]─────┐
         1       β”‚
                 β”‚ writes
                 N
      [POST]───┐ β”‚
        N      β”‚ β”‚
               β”‚ β”‚
      [CATEGORY] β”‚
         1      β”‚
               Mβ”‚
               β”‚N
          [TAG]


G. Praktikum: Desain ERD Blog & Portfolio (60 menit)

Kelompok 3-4 orang:

  1. Analisis kebutuhan sistem:
    • Fitur utama blog
    • Fitur portfolio
    • User roles (admin, writer, guest)
  2. Buat entity list lengkap:
    • Minimal 6 entity
    • Sertakan semua atribut
  3. Tentukan relationship:
    • Jenis relationship (1:1, 1:N, N:M)
    • Cardinality
    • Foreign keys
  4. Presentasi desain:
    • Presentasikan ERD ke kelas
    • Diskusikan keputusan desain

🎯 TUGAS PERTEMUAN 3

  1. Buat ERD lengkap untuk sistem blog+portfolio dengan:
    • Minimal 6 entity
    • Semua atribut penting
    • Relationship yang jelas
    • Cardinality tepat
  2. Gunakan tool (draw.io, Lucidchart, atau kertas scan)
  3. Siapkan presentasi 5 menit untuk pertemuan berikutnya

πŸ“š PERTEMUAN 4 – NORMALISASI & RELASI

Durasi: 3 jam (Teori: 1 jam, Praktik: 2 jam)

🎯 TUJUAN PEMBELAJARAN

  1. Memahami konsep normalisasi database
  2. Menerapkan 1NF, 2NF, 3NF
  3. Mendesain tabel junction untuk many-to-many

πŸ“– MATERI DETAIL

A. Review & Presentasi ERD (45 menit)

  • Presentasi tugas ERD per kelompok
  • Diskusi dan feedback
  • Refinement ERD berdasarkan masukan


B. Anomali Data (30 menit)

Contoh tabel tidak ternormalisasi:

sql

-- TABEL TIDAK NORMAL (bermasalah)
CREATE TABLE post_data (
    post_id INT,
    title VARCHAR(200),
    author_name VARCHAR(100),
    author_email VARCHAR(100),
    category_name VARCHAR(50),
    tag1 VARCHAR(30),
    tag2 VARCHAR(30),
    tag3 VARCHAR(30)
);

Masalah (Anomali):

  1. Insertion Anomaly: Tidak bisa insert author tanpa post
  2. Update Anomaly: Ubah email author harus update banyak row
  3. Deletion Anomaly: Hapus post, ikut hapus data author


C. 1NF - First Normal Form (30 menit)

Rules:

  1. Setiap kolom atomic (tidak bisa dibagi lagi)
  2. Tidak ada repeating groups
  3. Primary key terdefinisi

Contoh perbaikan:

sql

-- MASALAH: repeating groups (tag1, tag2, tag3)
-- SOLUSI: Buat tabel terpisah untuk tags


D. 2NF - Second Normal Form (30 menit)

Rules:

  1. Sudah memenuhi 1NF
  2. Semua non-key attribute bergantung sepenuhnya pada PK

Contoh:

sql

-- MASALAH: author_name tergantung author_id, bukan post_id
-- author_email juga tergantung author_id
-- SOLUSI: Pisahkan ke tabel authors


E. 3NF - Third Normal Form (30 menit)

Rules:

  1. Sudah memenuhi 2NF
  2. Tidak ada transitive dependency (A→B→C)

Contoh:

sql

-- MASALAH: category_description tergantung category_name
-- yang tergantung post_id (transitive)
-- SOLUSI: Pisahkan ke tabel categories


F. Tabel Junction (Many-to-Many) (45 menit)

Studi Kasus: Post - Tag:

sql

-- Post dan Tag: Many-to-Many relationship
-- SOLUSI: Buat junction table

-- Tabel posts
CREATE TABLE posts (
    id INT PRIMARY KEY AUTO_INCREMENT,
    title VARCHAR(200),
    content TEXT
);

-- Tabel tags
CREATE TABLE tags (
    id INT PRIMARY KEY AUTO_INCREMENT,
    name VARCHAR(50) UNIQUE
);

-- JUNCTION TABLE: post_tag
CREATE TABLE post_tag (
    post_id INT,
    tag_id INT,
    PRIMARY KEY (post_id, tag_id),
    FOREIGN KEY (post_id) REFERENCES posts(id) ON DELETE CASCADE,
    FOREIGN KEY (tag_id) REFERENCES tags(id) ON DELETE CASCADE
);


G. Studi Kasus Lengkap: Normalisasi Sistem Blog (60 menit)

Dari ERD ke Tabel Normalized:

  1. Tabel users:
    1. sql
    2. CREATE TABLE users (
          id INT PRIMARY KEY AUTO_INCREMENT,
          username VARCHAR(50) UNIQUE NOT NULL,
          email VARCHAR(100) UNIQUE NOT NULL,
          password_hash VARCHAR(255) NOT NULL,
          full_name VARCHAR(100),
          bio TEXT,
          avatar_url VARCHAR(255),
          role ENUM('admin', 'author', 'guest') DEFAULT 'guest',
          created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
          updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
      );
  2. Tabel categories:
    1. sql
    2. CREATE TABLE categories (
          id INT PRIMARY KEY AUTO_INCREMENT,
          name VARCHAR(50) UNIQUE NOT NULL,
          slug VARCHAR(60) UNIQUE NOT NULL,
          description TEXT,
          parent_id INT NULL,
          FOREIGN KEY (parent_id) REFERENCES categories(id) ON DELETE SET NULL,
          created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
      );
  3. Tabel posts:
    1. sql
    2. CREATE TABLE posts (
          id INT PRIMARY KEY AUTO_INCREMENT,
          title VARCHAR(200) NOT NULL,
          slug VARCHAR(220) UNIQUE NOT NULL,
          excerpt TEXT,
          content LONGTEXT NOT NULL,
          featured_image VARCHAR(255),
          status ENUM('draft', 'published', 'archived') DEFAULT 'draft',
          published_at TIMESTAMP NULL,
          user_id INT NOT NULL,
          category_id INT,
          view_count INT DEFAULT 0,
          meta_title VARCHAR(200),
          meta_description TEXT,
          created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
          updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
          
          FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
          FOREIGN KEY (category_id) REFERENCES categories(id) ON DELETE SET NULL
      );
  4. Tabel tags & post_tag:
    1. sql
    2. CREATE TABLE tags (
          id INT PRIMARY KEY AUTO_INCREMENT,
          name VARCHAR(50) UNIQUE NOT NULL,
          slug VARCHAR(60) UNIQUE NOT NULL
      );
      
      CREATE TABLE post_tag (
          post_id INT NOT NULL,
          tag_id INT NOT NULL,
          PRIMARY KEY (post_id, tag_id),
          FOREIGN KEY (post_id) REFERENCES posts(id) ON DELETE CASCADE,
          FOREIGN KEY (tag_id) REFERENCES tags(id) ON DELETE CASCADE
      );
  5. Tabel comments:
    1. sql
    2. CREATE TABLE comments (
          id INT PRIMARY KEY AUTO_INCREMENT,
          content TEXT NOT NULL,
          status ENUM('pending', 'approved', 'spam') DEFAULT 'pending',
          user_id INT NULL,
          post_id INT NOT NULL,
          parent_id INT NULL,
          author_name VARCHAR(100),
          author_email VARCHAR(100),
          author_ip VARCHAR(45),
          created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
          
          FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE SET NULL,
          FOREIGN KEY (post_id) REFERENCES posts(id) ON DELETE CASCADE,
          FOREIGN KEY (parent_id) REFERENCES comments(id) ON DELETE CASCADE
      );
  6. Tabel portfolios:
    1. sql
    2. CREATE TABLE portfolios (
          id INT PRIMARY KEY AUTO_INCREMENT,
          title VARCHAR(200) NOT NULL,
          slug VARCHAR(220) UNIQUE NOT NULL,
          description TEXT,
          project_url VARCHAR(255),
          client_name VARCHAR(100),
          project_date DATE,
          featured_image VARCHAR(255),
          gallery_images JSON,
          user_id INT NOT NULL,
          created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
          updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
          
          FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
      );


H. Praktikum: Normalisasi ERD ke Skema SQL (60 menit)

Tugas Individu:

  1. Pilih salah satu:
    • Sistem e-commerce mini
    • Sistem perpustakaan
    • Sistem manajemen tugas
  2. Lakukan:
    • Buat ERD (diagram)
    • Lakukan normalisasi hingga 3NF
    • Tulis DDL SQL lengkap
  3. Checklist:
    • Semua tabel punya PK
    • FK terdefinisi dengan baik
    • Junction table untuk N:M
    • Data types tepat
    • Constraints (NOT NULL, UNIQUE, dll)

🎯 TUGAS PERTEMUAN 4

  1. Buat skema database lengkap untuk sistem blog+portfolio:
    • Semua tabel dari studi kasus
    • Dengan normalisasi 3NF
    • Include semua constraints
  2. Export sebagai file .sql
  3. Testing di TablePlus:
    • Create database
    • Run script SQL
    • Verifikasi struktur

πŸ“š PERTEMUAN 5 – SQL LANJUTAN & TRIGGER

Durasi: 3 jam (Teori: 1 jam, Praktik: 2 jam)

🎯 TUJUAN PEMBELAJARAN

  1. Menguasai SQL DDL dan DML lanjutan
  2. Memahami penggunaan index, view, trigger
  3. Menerapkan constraint dan cascading

πŸ“– MATERI DETAIL

A. Review & Q&A (30 menit)

  • Diskusi tugas normalisasi
  • Troubleshooting SQL script
  • Best practices naming convention


B. DDL vs DML (30 menit)

DDL (Data Definition Language):

sql

-- CREATE
CREATE DATABASE blog_db;
CREATE TABLE users (...);

-- ALTER
ALTER TABLE posts ADD COLUMN reading_time INT;

-- DROP
DROP TABLE temp_data;

-- TRUNCATE (hapus semua data, reset auto_increment)
TRUNCATE TABLE logs;

DML (Data Manipulation Language):

sql

-- INSERT
INSERT INTO users (username, email) 
VALUES ('budi', 'budi@email.com');

-- UPDATE
UPDATE posts SET view_count = view_count + 1 
WHERE id = 5;

-- DELETE
DELETE FROM comments 
WHERE status = 'spam' AND created_at < '2024-01-01';

-- SELECT
SELECT * FROM posts WHERE status = 'published';

C. Indexes (30 menit)

Untuk optimasi query:

sql

-- Single column index
CREATE INDEX idx_posts_status ON posts(status);
CREATE INDEX idx_posts_user ON posts(user_id);

-- Composite index (untuk query multi-kolom)
CREATE INDEX idx_posts_user_status ON posts(user_id, status);

-- Unique index
CREATE UNIQUE INDEX idx_users_username ON users(username);

-- Full-text index (untuk search)
ALTER TABLE posts 
ADD FULLTEXT idx_fulltext_search (title, content);

-- Query dengan full-text
SELECT * FROM posts 
WHERE MATCH(title, content) AGAINST('laravel tutorial' IN NATURAL LANGUAGE MODE);

D. Views (30 menit)

Virtual tables untuk simplify query:

sql

-- View untuk post yang published
CREATE VIEW v_published_posts AS
SELECT 
    p.id,
    p.title,
    p.slug,
    p.excerpt,
    p.featured_image,
    p.published_at,
    u.username as author_name,
    c.name as category_name
FROM posts p
JOIN users u ON p.user_id = u.id
LEFT JOIN categories c ON p.category_id = c.id
WHERE p.status = 'published'
ORDER BY p.published_at DESC;

-- Penggunaan view
SELECT * FROM v_published_posts 
WHERE category_name = 'Teknologi';

-- View untuk statistic
CREATE VIEW v_blog_statistics AS
SELECT 
    (SELECT COUNT(*) FROM posts WHERE status = 'published') as total_posts,
    (SELECT COUNT(*) FROM users) as total_users,
    (SELECT COUNT(*) FROM comments WHERE status = 'approved') as total_comments,
    (SELECT SUM(view_count) FROM posts) as total_views;

E. Triggers (60 menit)

1. Auto-generate slug:

sql

DELIMITER $$

CREATE TRIGGER tr_posts_before_insert 
BEFORE INSERT ON posts 
FOR EACH ROW
BEGIN
    IF NEW.slug IS NULL OR NEW.slug = '' THEN
        -- Convert title to slug
        SET NEW.slug = LOWER(REPLACE(NEW.title, ' ', '-'));
        SET NEW.slug = REGEXP_REPLACE(NEW.slug, '[^a-z0-9-]', '');
        
        -- Ensure uniqueness
        SET @counter = 1;
        SET @base_slug = NEW.slug;
        
        WHILE EXISTS (SELECT 1 FROM posts WHERE slug = NEW.slug) DO
            SET NEW.slug = CONCAT(@base_slug, '-', @counter);
            SET @counter = @counter + 1;
        END WHILE;
    END IF;
END$$

DELIMITER ;

2. Activity log trigger:

sql

CREATE TABLE activity_logs (
    id INT PRIMARY KEY AUTO_INCREMENT,
    table_name VARCHAR(50) NOT NULL,
    record_id INT NOT NULL,
    action ENUM('INSERT', 'UPDATE', 'DELETE') NOT NULL,
    old_data JSON,
    new_data JSON,
    user_id INT,
    ip_address VARCHAR(45),
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

DELIMITER $$

CREATE TRIGGER tr_posts_after_update
AFTER UPDATE ON posts
FOR EACH ROW
BEGIN
    INSERT INTO activity_logs (
        table_name, 
        record_id, 
        action, 
        old_data, 
        new_data,
        user_id
    ) VALUES (
        'posts',
        OLD.id,
        'UPDATE',
        JSON_OBJECT(
            'title', OLD.title,
            'status', OLD.status,
            'content', LEFT(OLD.content, 500)
        ),
        JSON_OBJECT(
            'title', NEW.title,
            'status', NEW.status,
            'content', LEFT(NEW.content, 500)
        ),
        NEW.user_id
    );
END$$

DELIMITER ;

3. Update timestamp trigger:

sql

DELIMITER $$

CREATE TRIGGER tr_posts_update_timestamp
BEFORE UPDATE ON posts
FOR EACH ROW
BEGIN
    SET NEW.updated_at = CURRENT_TIMESTAMP;
END$$

DELIMITER ;

F. Constraints & Cascading (30 menit)

sql

-- Foreign Key dengan berbagai cascade options
CREATE TABLE posts (
    -- ...
    user_id INT NOT NULL,
    category_id INT,
    
    -- CASCADE: hapus user, hapus semua postnya
    FOREIGN KEY (user_id) 
        REFERENCES users(id) 
        ON DELETE CASCADE,
    
    -- SET NULL: hapus category, set category_id jadi NULL
    FOREIGN KEY (category_id) 
        REFERENCES categories(id) 
        ON DELETE SET NULL,
    
    -- NO ACTION: default, gagal jika ada reference
    FOREIGN KEY (category_id) 
        REFERENCES categories(id) 
        ON DELETE NO ACTION,
    
    -- RESTRICT: sama seperti NO ACTION
    FOREIGN KEY (category_id) 
        REFERENCES categories(id) 
        ON DELETE RESTRICT
);

-- Check constraint (MySQL 8.0+)
ALTER TABLE posts 
ADD CONSTRAINT chk_post_status 
CHECK (status IN ('draft', 'published', 'archived'));

ALTER TABLE users
ADD CONSTRAINT chk_user_email 
CHECK (email LIKE '%@%.%');

G. Praktikum Langsung di MySQL (90 menit)

Skenario: Implementasi Database Blog:

  1. Setup database:
    1. sql
    2. CREATE DATABASE blog_portfolio;
      USE blog_portfolio;
  2. Buat semua tabel (gunakan script dari pertemuan 4)
  3. Tambahkan indexes:
    1. sql
    2. -- Index untuk query yang sering digunakan
      CREATE INDEX idx_posts_published ON posts(published_at, status);
      CREATE INDEX idx_comments_post ON comments(post_id, status);
      CREATE INDEX idx_portfolios_user ON portfolios(user_id);
  4. Buat views:
    1. sql
    2. -- View untuk dashboard admin
      CREATE VIEW v_admin_dashboard AS
      SELECT 
          (SELECT COUNT(*) FROM posts) as total_posts,
          (SELECT COUNT(*) FROM posts WHERE status = 'published') as published_posts,
          (SELECT COUNT(*) FROM users) as total_users,
          (SELECT COUNT(*) FROM comments) as total_comments,
          (SELECT COUNT(*) FROM portfolios) as total_portfolios;
      
      -- View untuk recent activity
      CREATE VIEW v_recent_activity AS
      SELECT 'post' as type, title as description, created_at 
      FROM posts 
      UNION ALL
      SELECT 'comment' as type, CONCAT('Comment on post #', post_id) as description, created_at 
      FROM comments
      ORDER BY created_at DESC
      LIMIT 20;
  5. Implement triggers:
    1. sql
    2. -- Trigger untuk auto-slug categories
      DELIMITER $$
      CREATE TRIGGER tr_categories_before_insert
      BEFORE INSERT ON categories
      FOR EACH ROW
      BEGIN
          IF NEW.slug IS NULL THEN
              SET NEW.slug = LOWER(REPLACE(NEW.name, ' ', '-'));
          END IF;
      END$$
      DELIMITER ;
      
      -- Trigger untuk update view count
      DELIMITER $$
      CREATE TRIGGER tr_posts_after_select
      AFTER SELECT ON posts
      FOR EACH ROW
      BEGIN
          -- Ini contoh konsep, implementasi sebenarnya butuh application logic
          -- atau stored procedure
      END$$
      DELIMITER ;
  6. Insert sample data:
    1. sql
    2. -- Insert users
      INSERT INTO users (username, email, password_hash, role) VALUES
      ('admin', 'admin@blog.com', 'hashed_password', 'admin'),
      ('writer1', 'writer1@blog.com', 'hashed_password', 'author'),
      ('guest1', 'guest1@blog.com', 'hashed_password', 'guest');
      
      -- Insert categories
      INSERT INTO categories (name, slug) VALUES
      ('Teknologi', 'teknologi'),
      ('Desain', 'desain'),
      ('Bisnis', 'bisnis');
      
      -- Insert posts
      INSERT INTO posts (title, content, user_id, category_id, status, published_at) VALUES
      ('Belajar Database', 'Content...', 2, 1, 'published', NOW()),
      ('Tips Desain UI', 'Content...', 2, 2, 'published', NOW());
      
      -- Insert tags
      INSERT INTO tags (name, slug) VALUES
      ('PHP', 'php'),
      ('MySQL', 'mysql'),
      ('Laravel', 'laravel');
      
      -- Insert post_tag relationships
      INSERT INTO post_tag (post_id, tag_id) VALUES
      (1, 1), (1, 2),  -- Post 1 punya tag PHP dan MySQL
      (2, 3);          -- Post 2 punya tag Laravel
  7. Test queries kompleks:
    1. sql
    2. -- Query: Dapatkan post dengan detail author, category, dan tags
      SELECT 
          p.title,
          p.published_at,
          u.username as author,
          c.name as category,
          GROUP_CONCAT(t.name SEPARATOR ', ') as tags
      FROM posts p
      JOIN users u ON p.user_id = u.id
      LEFT JOIN categories c ON p.category_id = c.id
      LEFT JOIN post_tag pt ON p.id = pt.post_id
      LEFT JOIN tags t ON pt.tag_id = t.id
      WHERE p.status = 'published'
      GROUP BY p.id
      ORDER BY p.published_at DESC;
      
      -- Query: Statistik post per kategori
      SELECT 
          c.name as category,
          COUNT(p.id) as post_count,
          SUM(p.view_count) as total_views
      FROM categories c
      LEFT JOIN posts p ON c.id = p.category_id AND p.status = 'published'
      GROUP BY c.id
      ORDER BY post_count DESC;
  8. Test triggers:
    1. sql
    2. -- Test auto-slug trigger
      INSERT INTO posts (title, content, user_id, slug) 
      VALUES ('Judul Test', 'Content...', 2, '');
      -- Cek: slug akan otomatis terisi 'judul-test'
      
      -- Test activity log
      UPDATE posts SET title = 'Judul Updated' WHERE id = 1;
      -- Cek tabel activity_logs
      SELECT * FROM activity_logs;


🎯 TUGAS AKHIR FASE 1

Buat database lengkap untuk sistem Blog & Portfolio:

  1. File 1: database_design.pdf:
    • ERD lengkap dengan semua entity
    • Relationship dan cardinality
    • Penjelasan normalisasi yang dilakukan
  2. File 2: schema.sql:
    • DDL lengkap semua tabel
    • Indexes yang diperlukan
    • Constraints (FK, CHECK, UNIQUE)
    • Views yang berguna
  3. File 3: triggers.sql:
    • Minimal 3 trigger:
      • Auto-generate slug
      • Activity logging
      • Auto-update timestamp
    • Penjelasan masing-masing trigger
  4. File 4: sample_data.sql:
    • Insert sample data minimal:
      • 3 users (admin, author, guest)
      • 5 categories
      • 10 posts dengan status berbeda
      • 8 tags
      • Post-tag relationships
      • 5 comments
      • 3 portfolio items
  5. File 5: queries.sql:
    • 5 query kompleks untuk:
      • Dashboard statistics
      • Recent posts with tags
      • Search functionality
      • User activity report
      • Popular posts

Delivery:

  • Upload ke GitHub/GitLab repository
  • Presentasi 10 menit di awal Fase 2
  • Siap untuk diimplementasi di Laravel

🎯 EVALUASI FASE 1

Kriteria Penilaian:

  1. Konsep Database (40%):
    • ERD yang benar dan lengkap
    • Normalisasi tepat (3NF)
    • Relationship design yang baik
  2. Implementasi SQL (40%):
    • Syntax SQL yang benar
    • Penggunaan constraint tepat
    • Trigger dan view relevan
  3. Best Practices (20%):
    • Naming convention konsisten
    • Dokumentasi jelas
    • Struktur folder rapi

Kelulusan Minimum:

  • Memahami konsep database relasional
  • Mampu membuat ERD sederhana
  • Bisa menulis query SQL dasar
  • Siap masuk ke Laravel dengan pemahaman database yang kuat

πŸ“š RESOURCES TAMBAHAN

Bacaan Wajib:

  1. MySQL Documentation
  2. Database Normalization Explained
  3. SQL Style Guide

Tools Praktikum:

  1. Diagram: draw.io, Lucidchart, MySQL Workbench
  2. SQL Playground: SQL Fiddle, DB Fiddle
  3. Local Practice: TablePlus + Herd

Latihan Tambahan:

  1. SQLZoo (https://sqlzoo.net/)
  2. LeetCode Database Problems
  3. HackerRank SQL Track



Dilihat

210 kali

Trending

11