Menampilkan Data dari Database PHP: Tutorial Lengkap untuk Developer

By | August 16, 2025

Table of Contents

Menampilkan Data dari Database PHP: Tutorial Lengkap untuk Developer

Dalam dunia web development, menampilkan data dari database PHP adalah skill fundamental yang wajib dikuasai setiap developer. Kemampuan untuk mengkoneksikan aplikasi PHP dengan database MySQL dan menampilkan data secara dinamis merupakan pondasi dari hampir semua aplikasi web modern.

Tutorial ini akan memandu Anda step-by-step untuk menguasai teknik menampilkan data dari database menggunakan PHP, mulai dari koneksi database dasar hingga implementasi yang advanced dengan berbagai metode seperti MySQLi dan PDO. Anda akan belajar best practices, security measures, dan optimization techniques yang digunakan oleh developer profesional.

🎯 Apa yang Akan Anda Pelajari?

  • Koneksi database MySQL dengan PHP (MySQLi & PDO)
  • Menampilkan data dalam berbagai format (table, list, card)
  • Implementasi pagination dan search functionality
  • Security best practices (SQL Injection prevention)
  • Error handling dan debugging techniques
  • Performance optimization untuk query database
  • AJAX integration untuk dynamic data loading

📋 Persiapan dan Requirements

Sebelum memulai tutorial menampilkan data dari database PHP, pastikan Anda sudah menyiapkan environment development berikut:

🛠️ Tools yang Dibutuhkan

  • XAMPP/WAMP/MAMP: Local server dengan Apache, MySQL, dan PHP
  • MySQL Database: Database server untuk menyimpan data
  • phpMyAdmin: Interface untuk mengelola database MySQL
  • Text Editor: VS Code, PhpStorm, Sublime Text, atau editor favorit
  • Web Browser: Chrome, Firefox dengan developer tools

📚 Pengetahuan Dasar

  • HTML dan CSS fundamental
  • PHP basic syntax dan programming concepts
  • MySQL database dan SQL queries
  • HTTP request/response cycle

🗄️ Setup Database MySQL

Mari mulai dengan membuat database dan tabel sample untuk tutorial menampilkan data dari database PHP:

📦 Membuat Database dan Tabel

Buka phpMyAdmin dan jalankan SQL berikut untuk membuat database dan tabel sample:

-- Membuat database
CREATE DATABASE IF NOT EXISTS tutorial_php;
USE tutorial_php;

-- Membuat tabel users
CREATE TABLE users (
    id INT AUTO_INCREMENT PRIMARY KEY,
    nama VARCHAR(100) NOT NULL,
    email VARCHAR(100) UNIQUE NOT NULL,
    umur INT NOT NULL,
    kota VARCHAR(50) NOT NULL,
    pekerjaan VARCHAR(100) NOT NULL,
    gaji DECIMAL(10,2) DEFAULT 0,
    tanggal_daftar TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    status ENUM('aktif', 'nonaktif') DEFAULT 'aktif'
);

-- Insert sample data
INSERT INTO users (nama, email, umur, kota, pekerjaan, gaji) VALUES
('Ahmad Rizki', 'ahmad.rizki@email.com', 28, 'Jakarta', 'Web Developer', 8500000),
('Siti Nurhaliza', 'siti.nur@email.com', 25, 'Bandung', 'UI/UX Designer', 7500000),
('Budi Santoso', 'budi.santoso@email.com', 32, 'Surabaya', 'Project Manager', 12000000),
('Maya Sari', 'maya.sari@email.com', 29, 'Yogyakarta', 'Data Analyst', 9000000),
('Eko Prasetyo', 'eko.prasetyo@email.com', 26, 'Medan', 'Backend Developer', 8000000),
('Rina Wati', 'rina.wati@email.com', 31, 'Makassar', 'Frontend Developer', 7800000),
('Doni Setiawan', 'doni.setiawan@email.com', 27, 'Palembang', 'DevOps Engineer', 10500000),
('Lisa Permata', 'lisa.permata@email.com', 24, 'Denpasar', 'Mobile Developer', 8200000);

📁 Struktur Folder Project

Buat struktur folder yang rapi untuk project PHP Anda:

php-database-tutorial/
├── config/
│   └── database.php
├── includes/
│   ├── header.php
│   └── footer.php
├── css/
│   └── style.css
├── js/
│   └── script.js
├── functions/
│   └── database_functions.php
├── pages/
│   ├── users_list.php
│   ├── user_detail.php
│   └── search_users.php
└── index.php

🔗 Koneksi Database dengan PHP

Ada beberapa cara untuk mengkoneksikan PHP dengan database MySQL. Mari kita bahas metode yang paling populer dan aman:

🌐 Metode 1: MySQLi (MySQL Improved)

MySQLi adalah extension PHP yang dirancang khusus untuk MySQL. Buat file konfigurasi database di config/database.php:

<?php
// config/database.php

// Konfigurasi database
define('DB_HOST', 'localhost');
define('DB_USER', 'root');
define('DB_PASS', '');
define('DB_NAME', 'tutorial_php');

// Fungsi koneksi database menggunakan MySQLi
function connectDatabase() {
    $connection = new mysqli(DB_HOST, DB_USER, DB_PASS, DB_NAME);
    
    // Cek koneksi
    if ($connection->connect_error) {
        die("Koneksi gagal: " . $connection->connect_error);
    }
    
    // Set charset untuk menghindari masalah encoding
    $connection->set_charset("utf8");
    
    return $connection;
}

// Fungsi untuk menutup koneksi
function closeDatabase($connection) {
    if ($connection) {
        $connection->close();
    }
}

// Test koneksi
function testConnection() {
    $conn = connectDatabase();
    if ($conn) {
        echo "Koneksi database berhasil!";
        closeDatabase($conn);
        return true;
    }
    return false;
}
?>

📦 Metode 2: PDO (PHP Data Objects) – Recommended

PDO adalah interface yang lebih fleksibel dan mendukung multiple database. Buat file konfigurasi PDO:

<?php
// config/database_pdo.php

class Database {
    private $host = 'localhost';
    private $db_name = 'tutorial_php';
    private $username = 'root';
    private $password = '';
    private $conn;
    
    // Koneksi database
    public function connect() {
        $this->conn = null;
        
        try {
            $dsn = "mysql:host=" . $this->host . ";dbname=" . $this->db_name . ";charset=utf8";
            $options = [
                PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
                PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
                PDO::ATTR_EMULATE_PREPARES => false,
            ];
            
            $this->conn = new PDO($dsn, $this->username, $this->password, $options);
            
        } catch(PDOException $e) {
            echo "Koneksi gagal: " . $e->getMessage();
        }
        
        return $this->conn;
    }
    
    // Tutup koneksi
    public function disconnect() {
        $this->conn = null;
    }
    
    // Test koneksi
    public function testConnection() {
        $conn = $this->connect();
        if ($conn) {
            echo "Koneksi PDO berhasil!";
            return true;
        }
        return false;
    }
}
?>

Kemudian buat file untuk test koneksi:

<?php
// test_connection.php
require_once 'config/database.php';
require_once 'config/database_pdo.php';

echo "<h2>Test Koneksi MySQLi:</h2>";
testConnection();

echo "<h2>Test Koneksi PDO:</h2>";
$db = new Database();
$db->testConnection();
?>

📊 Menampilkan Data dengan MySQLi

Sekarang mari belajar cara menampilkan data dari database PHP menggunakan MySQLi dengan berbagai format tampilan:

📁 File: functions/database_functions.php

<?php
// functions/database_functions.php
require_once '../config/database.php';

/**
 * Fungsi untuk mengambil semua data users
 */
function getAllUsers() {
    $conn = connectDatabase();
    $users = [];
    
    $sql = "SELECT * FROM users ORDER BY tanggal_daftar DESC";
    $result = $conn->query($sql);
    
    if ($result->num_rows > 0) {
        while($row = $result->fetch_assoc()) {
            $users[] = $row;
        }
    }
    
    closeDatabase($conn);
    return $users;
}

/**
 * Fungsi untuk mengambil data user berdasarkan ID
 */
function getUserById($id) {
    $conn = connectDatabase();
    $user = null;
    
    // Menggunakan prepared statement untuk keamanan
    $stmt = $conn->prepare("SELECT * FROM users WHERE id = ?");
    $stmt->bind_param("i", $id);
    $stmt->execute();
    
    $result = $stmt->get_result();
    if ($result->num_rows > 0) {
        $user = $result->fetch_assoc();
    }
    
    $stmt->close();
    closeDatabase($conn);
    return $user;
}

/**
 * Fungsi untuk search users berdasarkan nama atau email
 */
function searchUsers($keyword) {
    $conn = connectDatabase();
    $users = [];
    
    $searchTerm = "%" . $keyword . "%";
    $stmt = $conn->prepare("SELECT * FROM users WHERE nama LIKE ? OR email LIKE ? ORDER BY nama ASC");
    $stmt->bind_param("ss", $searchTerm, $searchTerm);
    $stmt->execute();
    
    $result = $stmt->get_result();
    while($row = $result->fetch_assoc()) {
        $users[] = $row;
    }
    
    $stmt->close();
    closeDatabase($conn);
    return $users;
}

/**
 * Fungsi untuk pagination
 */
function getUsersWithPagination($page = 1, $limit = 5) {
    $conn = connectDatabase();
    $offset = ($page - 1) * $limit;
    
    // Hitung total records
    $countSql = "SELECT COUNT(*) as total FROM users";
    $countResult = $conn->query($countSql);
    $totalRecords = $countResult->fetch_assoc()['total'];
    
    // Ambil data dengan limit
    $stmt = $conn->prepare("SELECT * FROM users ORDER BY tanggal_daftar DESC LIMIT ? OFFSET ?");
    $stmt->bind_param("ii", $limit, $offset);
    $stmt->execute();
    
    $result = $stmt->get_result();
    $users = [];
    while($row = $result->fetch_assoc()) {
        $users[] = $row;
    }
    
    $stmt->close();
    closeDatabase($conn);
    
    return [
        'users' => $users,
        'total' => $totalRecords,
        'pages' => ceil($totalRecords / $limit),
        'current_page' => $page
    ];
}

/**
 * Fungsi untuk format currency
 */
function formatRupiah($angka) {
    return "Rp " . number_format($angka, 0, ',', '.');
}

/**
 * Fungsi untuk format tanggal Indonesia
 */
function formatTanggalIndonesia($tanggal) {
    $bulan = [
        1 => 'Januari', 'Februari', 'Maret', 'April', 'Mei', 'Juni',
        'Juli', 'Agustus', 'September', 'Oktober', 'November', 'Desember'
    ];
    
    $timestamp = strtotime($tanggal);
    $hari = date('d', $timestamp);
    $bulanIndex = date('n', $timestamp);
    $tahun = date('Y', $timestamp);
    
    return $hari . ' ' . $bulan[$bulanIndex] . ' ' . $tahun;
}
?>

🎨 Membuat Interface untuk Menampilkan Data

Sekarang mari buat halaman web yang menampilkan data dari database dengan tampilan yang menarik:

📁 File: includes/header.php

<!DOCTYPE html>
<html lang="id">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title><?= $title ?? 'Tutorial PHP Database' ?></title>
    
    <!-- Bootstrap 5 CSS -->
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
    
    <!-- Font Awesome Icons -->
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
    
    <!-- Custom CSS -->
    <link rel="stylesheet" href="../css/style.css">
</head>
<body>
    <nav class="navbar navbar-expand-lg navbar-dark bg-primary">
        <div class="container">
            <a class="navbar-brand" href="../index.php">
                <i class="fas fa-database me-2"></i>
                PHP Database Tutorial
            </a>
            
            <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav">
                <span class="navbar-toggler-icon"></span>
            </button>
            
            <div class="collapse navbar-collapse" id="navbarNav">
                <ul class="navbar-nav ms-auto">
                    <li class="nav-item">
                        <a class="nav-link" href="../index.php">Home</a>
                    </li>
                    <li class="nav-item">
                        <a class="nav-link" href="users_list.php">Daftar Users</a>
                    </li>
                    <li class="nav-item">
                        <a class="nav-link" href="search_users.php">Cari Users</a>
                    </li>
                </ul>
            </div>
        </div>
    </nav>

    <main class="container mt-4">

📁 File: pages/users_list.php

<?php
require_once '../functions/database_functions.php';

// Ambil parameter pagination
$page = isset($_GET['page']) ? (int)$_GET['page'] : 1;
$limit = 5;

// Ambil data users dengan pagination
$result = getUsersWithPagination($page, $limit);
$users = $result['users'];
$totalPages = $result['pages'];
$currentPage = $result['current_page'];

$title = "Daftar Users - Halaman $currentPage";
include '../includes/header.php';
?>

<div class="row">
    <div class="col-12">
        <div class="d-flex justify-content-between align-items-center mb-4">
            <h1 class="h3">
                <i class="fas fa-users text-primary me-2"></i>
                Daftar Users
            </h1>
            <div>
                <span class="badge bg-info">Total: <?= $result['total'] ?> users</span>
            </div>
        </div>

        <!-- Search Form -->
        <div class="card mb-4">
            <div class="card-body">
                <form method="GET" action="search_users.php" class="row g-3">
                    <div class="col-md-8">
                        <input type="text" class="form-control" name="keyword" 
                               placeholder="Cari berdasarkan nama atau email..." 
                               value="<?= htmlspecialchars($_GET['keyword'] ?? '') ?>">
                    </div>
                    <div class="col-md-4">
                        <button type="submit" class="btn btn-primary w-100">
                            <i class="fas fa-search me-1"></i> Cari
                        </button>
                    </div>
                </form>
            </div>
        </div>

        <!-- Users Table -->
        <div class="card">
            <div class="card-header bg-primary text-white">
                <h5 class="mb-0">
                    <i class="fas fa-table me-2"></i>
                    Data Users (Halaman <?= $currentPage ?> dari <?= $totalPages ?>)
                </h5>
            </div>
            <div class="card-body p-0">
                <?php if (!empty($users)): ?>
                <div class="table-responsive">
                    <table class="table table-hover mb-0">
                        <thead class="table-light">
                            <tr>
                                <th>#</th>
                                <th>Nama</th>
                                <th>Email</th>
                                <th>Umur</th>
                                <th>Kota</th>
                                <th>Pekerjaan</th>
                                <th>Gaji</th>
                                <th>Status</th>
                                <th>Aksi</th>
                            </tr>
                        </thead>
                        <tbody>
                            <?php foreach ($users as $index => $user): ?>
                            <tr>
                                <td><?= (($currentPage - 1) * $limit) + $index + 1 ?></td>
                                <td>
                                    <div class="d-flex align-items-center">
                                        <div class="avatar-sm bg-primary text-white rounded-circle d-flex align-items-center justify-content-center me-2">
                                            <?= strtoupper(substr($user['nama'], 0, 1)) ?>
                                        </div>
                                        <strong><?= htmlspecialchars($user['nama']) ?></strong>
                                    </div>
                                </td>
                                <td><?= htmlspecialchars($user['email']) ?></td>
                                <td><?= $user['umur'] ?> tahun</td>
                                <td><?= htmlspecialchars($user['kota']) ?></td>
                                <td><?= htmlspecialchars($user['pekerjaan']) ?></td>
                                <td><?= formatRupiah($user['gaji']) ?></td>
                                <td>
                                    <span class="badge bg-<?= $user['status'] == 'aktif' ? 'success' : 'secondary' ?>">
                                        <?= ucfirst($user['status']) ?>
                                    </span>
                                </td>
                                <td>
                                    <a href="user_detail.php?id=<?= $user['id'] ?>" class="btn btn-sm btn-outline-primary">
                                        <i class="fas fa-eye"></i> Detail
                                    </a>
                                </td>
                            </tr>
                            <?php endforeach; ?>
                        </tbody>
                    </table>
                </div>
                <?php else: ?>
                <div class="text-center py-5">
                    <i class="fas fa-users fa-3x text-muted mb-3"></i>
                    <h5 class="text-muted">Tidak ada data users</h5>
                </div>
                <?php endif; ?>
            </div>
        </div>

        <!-- Pagination -->
        <?php if ($totalPages > 1): ?>
        <nav aria-label="Page navigation" class="mt-4">
            <ul class="pagination justify-content-center">
                <li class="page-item <?= $currentPage == 1 ? 'disabled' : '' ?>">
                    <a class="page-link" href="?page=<?= $currentPage - 1 ?>">
                        <i class="fas fa-chevron-left"></i> Previous
                    </a>
                </li>
                
                <?php for ($i = 1; $i <= $totalPages; $i++): ?>
                <li class="page-item <?= $i == $currentPage ? 'active' : '' ?>">
                    <a class="page-link" href="?page=<?= $i ?>"><?= $i ?></a>
                </li>
                <?php endfor; ?>
                
                <li class="page-item <?= $currentPage == $totalPages ? 'disabled' : '' ?>">
                    <a class="page-link" href="?page=<?= $currentPage + 1 ?>">
                        Next <i class="fas fa-chevron-right"></i>
                    </a>
                </li>
            </ul>
        </nav>
        <?php endif; ?>
    </div>
</div>

<?php include '../includes/footer.php'; ?>

🔒 Menampilkan Data dengan PDO (Recommended)

PDO adalah metode yang lebih aman dan fleksibel untuk menampilkan data dari database PHP. Mari buat class untuk mengelola data users:

📁 File: classes/UserManager.php

<?php
// classes/UserManager.php
require_once '../config/database_pdo.php';

class UserManager {
    private $db;
    private $conn;
    
    public function __construct() {
        $this->db = new Database();
        $this->conn = $this->db->connect();
    }
    
    /**
     * Ambil semua users dengan pagination
     */
    public function getAllUsers($page = 1, $limit = 10) {
        try {
            $offset = ($page - 1) * $limit;
            
            // Hitung total records
            $countQuery = "SELECT COUNT(*) as total FROM users WHERE status = 'aktif'";
            $countStmt = $this->conn->prepare($countQuery);
            $countStmt->execute();
            $totalRecords = $countStmt->fetch()['total'];
            
            // Ambil data users
            $query = "SELECT * FROM users WHERE status = 'aktif' ORDER BY tanggal_daftar DESC LIMIT :limit OFFSET :offset";
            $stmt = $this->conn->prepare($query);
            $stmt->bindParam(':limit', $limit, PDO::PARAM_INT);
            $stmt->bindParam(':offset', $offset, PDO::PARAM_INT);
            $stmt->execute();
            
            $users = $stmt->fetchAll();
            
            return [
                'users' => $users,
                'total' => $totalRecords,
                'pages' => ceil($totalRecords / $limit),
                'current_page' => $page,
                'limit' => $limit
            ];
            
        } catch (PDOException $e) {
            error_log("Error in getAllUsers: " . $e->getMessage());
            return ['users' => [], 'total' => 0, 'pages' => 0, 'current_page' => 1];
        }
    }
    
    /**
     * Ambil user berdasarkan ID
     */
    public function getUserById($id) {
        try {
            $query = "SELECT * FROM users WHERE id = :id AND status = 'aktif'";
            $stmt = $this->conn->prepare($query);
            $stmt->bindParam(':id', $id, PDO::PARAM_INT);
            $stmt->execute();
            
            return $stmt->fetch();
            
        } catch (PDOException $e) {
            error_log("Error in getUserById: " . $e->getMessage());
            return null;
        }
    }
    
    /**
     * Search users berdasarkan keyword
     */
    public function searchUsers($keyword, $page = 1, $limit = 10) {
        try {
            $offset = ($page - 1) * $limit;
            $searchTerm = "%{$keyword}%";
            
            // Hitung total hasil search
            $countQuery = "SELECT COUNT(*) as total FROM users 
                          WHERE (nama LIKE :keyword1 OR email LIKE :keyword2 OR pekerjaan LIKE :keyword3) 
                          AND status = 'aktif'";
            $countStmt = $this->conn->prepare($countQuery);
            $countStmt->bindParam(':keyword1', $searchTerm);
            $countStmt->bindParam(':keyword2', $searchTerm);
            $countStmt->bindParam(':keyword3', $searchTerm);
            $countStmt->execute();
            $totalRecords = $countStmt->fetch()['total'];
            
            // Ambil hasil search
            $query = "SELECT * FROM users 
                     WHERE (nama LIKE :keyword1 OR email LIKE :keyword2 OR pekerjaan LIKE :keyword3) 
                     AND status = 'aktif'
                     ORDER BY nama ASC 
                     LIMIT :limit OFFSET :offset";
            $stmt = $this->conn->prepare($query);
            $stmt->bindParam(':keyword1', $searchTerm);
            $stmt->bindParam(':keyword2', $searchTerm);
            $stmt->bindParam(':keyword3', $searchTerm);
            $stmt->bindParam(':limit', $limit, PDO::PARAM_INT);
            $stmt->bindParam(':offset', $offset, PDO::PARAM_INT);
            $stmt->execute();
            
            $users = $stmt->fetchAll();
            
            return [
                'users' => $users,
                'total' => $totalRecords,
                'pages' => ceil($totalRecords / $limit),
                'current_page' => $page,
                'keyword' => $keyword
            ];
            
        } catch (PDOException $e) {
            error_log("Error in searchUsers: " . $e->getMessage());
            return ['users' => [], 'total' => 0, 'pages' => 0, 'current_page' => 1];
        }
    }
    
    /**
     * Ambil statistik users
     */
    public function getUserStats() {
        try {
            $stats = [];
            
            // Total users
            $query = "SELECT COUNT(*) as total FROM users WHERE status = 'aktif'";
            $stmt = $this->conn->prepare($query);
            $stmt->execute();
            $stats['total_users'] = $stmt->fetch()['total'];
            
            // Rata-rata umur
            $query = "SELECT AVG(umur) as avg_age FROM users WHERE status = 'aktif'";
            $stmt = $this->conn->prepare($query);
            $stmt->execute();
            $stats['avg_age'] = round($stmt->fetch()['avg_age'], 1);
            
            // Rata-rata gaji
            $query = "SELECT AVG(gaji) as avg_salary FROM users WHERE status = 'aktif'";
            $stmt = $this->conn->prepare($query);
            $stmt->execute();
            $stats['avg_salary'] = $stmt->fetch()['avg_salary'];
            
            // Kota terpopuler
            $query = "SELECT kota, COUNT(*) as count FROM users WHERE status = 'aktif' GROUP BY kota ORDER BY count DESC LIMIT 1";
            $stmt = $this->conn->prepare($query);
            $stmt->execute();
            $topCity = $stmt->fetch();
            $stats['top_city'] = $topCity ? $topCity['kota'] : 'N/A';
            
            return $stats;
            
        } catch (PDOException $e) {
            error_log("Error in getUserStats: " . $e->getMessage());
            return [];
        }
    }
    
    /**
     * Format currency Indonesia
     */
    public function formatRupiah($amount) {
        return 'Rp ' . number_format($amount, 0, ',', '.');
    }
    
    /**
     * Format tanggal Indonesia
     */
    public function formatDateIndonesia($date) {
        $months = [
            1 => 'Januari', 'Februari', 'Maret', 'April', 'Mei', 'Juni',
            'Juli', 'Agustus', 'September', 'Oktober', 'November', 'Desember'
        ];
        
        $timestamp = strtotime($date);
        $day = date('d', $timestamp);
        $month = $months[(int)date('n', $timestamp)];
        $year = date('Y', $timestamp);
        
        return "{$day} {$month} {$year}";
    }
    
    public function __destruct() {
        $this->db->disconnect();
    }
}
?>

📁 File: pages/user_detail.php

<?php
require_once '../classes/UserManager.php';

$userManager = new UserManager();
$userId = isset($_GET['id']) ? (int)$_GET['id'] : 0;

if ($userId <= 0) {
    header('Location: users_list.php');
    exit;
}

$user = $userManager->getUserById($userId);

if (!$user) {
    header('Location: users_list.php?error=user_not_found');
    exit;
}

$title = "Detail User - " . htmlspecialchars($user['nama']);
include '../includes/header.php';
?>

<div class="row">
    <div class="col-12">
        <nav aria-label="breadcrumb">
            <ol class="breadcrumb">
                <li class="breadcrumb-item"><a href="../index.php">Home</a></li>
                <li class="breadcrumb-item"><a href="users_list.php">Daftar Users</a></li>
                <li class="breadcrumb-item active">Detail User</li>
            </ol>
        </nav>
    </div>
</div>

<div class="row">
    <div class="col-lg-4">
        <div class="card">
            <div class="card-body text-center">
                <div class="avatar-lg bg-primary text-white rounded-circle d-flex align-items-center justify-content-center mx-auto mb-3" style="width: 100px; height: 100px; font-size: 2rem;">
                    <?= strtoupper(substr($user['nama'], 0, 2)) ?>
                </div>
                <h4 class="card-title"><?= htmlspecialchars($user['nama']) ?></h4>
                <p class="text-muted"><?= htmlspecialchars($user['pekerjaan']) ?></p>
                <span class="badge bg-<?= $user['status'] == 'aktif' ? 'success' : 'secondary' ?> fs-6">
                    <?= ucfirst($user['status']) ?>
                </span>
            </div>
        </div>
        
        <div class="card mt-4">
            <div class="card-header">
                <h5 class="mb-0">
                    <i class="fas fa-chart-bar me-2"></i>
                    Quick Stats
                </h5>
            </div>
            <div class="card-body">
                <div class="row text-center">
                    <div class="col-6">
                        <h4 class="text-primary"><?= $user['umur'] ?></h4>
                        <small class="text-muted">Tahun</small>
                    </div>
                    <div class="col-6">
                        <h4 class="text-success"><?= $userManager->formatRupiah($user['gaji']) ?></h4>
                        <small class="text-muted">Gaji</small>
                    </div>
                </div>
            </div>
        </div>
    </div>
    
    <div class="col-lg-8">
        <div class="card">
            <div class="card-header bg-primary text-white">
                <h5 class="mb-0">
                    <i class="fas fa-user me-2"></i>
                    Informasi Detail
                </h5>
            </div>
            <div class="card-body">
                <div class="row">
                    <div class="col-md-6">
                        <div class="mb-3">
                            <label class="form-label fw-bold">
                                <i class="fas fa-user text-primary me-2"></i>
                                Nama Lengkap
                            </label>
                            <p class="form-control-plaintext"><?= htmlspecialchars($user['nama']) ?></p>
                        </div>
                        
                        <div class="mb-3">
                            <label class="form-label fw-bold">
                                <i class="fas fa-envelope text-primary me-2"></i>
                                Email
                            </label>
                            <p class="form-control-plaintext">
                                <a href="mailto:<?= $user['email'] ?>"><?= htmlspecialchars($user['email']) ?></a>
                            </p>
                        </div>
                        
                        <div class="mb-3">
                            <label class="form-label fw-bold">
                                <i class="fas fa-birthday-cake text-primary me-2"></i>
                                Umur
                            </label>
                            <p class="form-control-plaintext"><?= $user['umur'] ?> tahun</p>
                        </div>
                        
                        <div class="mb-3">
                            <label class="form-label fw-bold">
                                <i class="fas fa-map-marker-alt text-primary me-2"></i>
                                Kota
                            </label>
                            <p class="form-control-plaintext"><?= htmlspecialchars($user['kota']) ?></p>
                        </div>
                    </div>
                    
                    <div class="col-md-6">
                        <div class="mb-3">
                            <label class="form-label fw-bold">
                                <i class="fas fa-briefcase text-primary me-2"></i>
                                Pekerjaan
                            </label>
                            <p class="form-control-plaintext"><?= htmlspecialchars($user['pekerjaan']) ?></p>
                        </div>
                        
                        <div class="mb-3">
                            <label class="form-label fw-bold">
                                <i class="fas fa-money-bill-wave text-primary me-2"></i>
                                Gaji
                            </label>
                            <p class="form-control-plaintext"><?= $userManager->formatRupiah($user['gaji']) ?></p>
                        </div>
                        
                        <div class="mb-3">
                            <label class="form-label fw-bold">
                                <i class="fas fa-calendar text-primary me-2"></i>
                                Tanggal Daftar
                            </label>
                            <p class="form-control-plaintext"><?= $userManager->formatDateIndonesia($user['tanggal_daftar']) ?></p>
                        </div>
                        
                        <div class="mb-3">
                            <label class="form-label fw-bold">
                                <i class="fas fa-toggle-on text-primary me-2"></i>
                                Status
                            </label>
                            <p class="form-control-plaintext">
                                <span class="badge bg-<?= $user['status'] == 'aktif' ? 'success' : 'secondary' ?> fs-6">
                                    <?= ucfirst($user['status']) ?>
                                </span>
                            </p>
                        </div>
                    </div>
                </div>
                
                <hr>
                
                <div class="d-flex gap-2">
                    <a href="users_list.php" class="btn btn-secondary">
                        <i class="fas fa-arrow-left me-1"></i> Kembali
                    </a>
                    <button class="btn btn-primary" onclick="window.print()">
                        <i class="fas fa-print me-1"></i> Print
                    </button>
                    <a href="mailto:<?= $user['email'] ?>" class="btn btn-success">
                        <i class="fas fa-envelope me-1"></i> Kirim Email
                    </a>
                </div>
            </div>
        </div>
    </div>
</div>

<?php include '../includes/footer.php'; ?>

🔒 Security dan Best Practices

🛡️ Keamanan Database PHP

1. SQL Injection Prevention
Selalu gunakan prepared statements untuk mencegah SQL injection attacks
2. Input Validation
Validasi semua input user menggunakan filter_var() dan htmlspecialchars()
3. Error Handling
Jangan tampilkan error database langsung ke user, gunakan error logging
4. Database Credentials
Simpan kredensial database di file terpisah dan jangan commit ke repository
5. Connection Security
Gunakan SSL/TLS untuk koneksi database dan limit user privileges

⚡ Performance Optimization

🚀 Tips Optimisasi Database

📊 Query Optimization

  • Indexing: Buat index pada kolom yang sering di-query (WHERE, ORDER BY)
  • LIMIT Clause: Selalu gunakan LIMIT untuk pagination dan mencegah memory overflow
  • SELECT Specific: Hindari SELECT *, pilih kolom yang diperlukan saja
  • Query Caching: Implement query result caching untuk data yang jarang berubah

🔄 Connection Management

  • Connection Pooling: Gunakan persistent connections dengan bijak
  • Close Connections: Selalu tutup koneksi database setelah selesai
  • Timeout Settings: Set connection timeout yang appropriate
  • Error Handling: Implement proper error handling dan retry logic

🔗 Artikel Terkait

Untuk memperdalam pemahaman PHP dan database MySQL, baca juga artikel-artikel berikut:

🛠️ Troubleshooting Common Issues

❌ Masalah Umum dan Solusinya

Problem: “Connection refused” error
Solution: Periksa apakah MySQL service berjalan, cek host/port, dan pastikan firewall tidak memblokir koneksi
Problem: “Access denied for user” error
Solution: Periksa username/password database, pastikan user memiliki privileges yang cukup
Problem: Data tidak tampil atau kosong
Solution: Debug query dengan var_dump(), periksa nama tabel/kolom, dan pastikan data ada di database
Problem: Encoding/charset issues
Solution: Set charset ke UTF-8 di koneksi database dan HTML meta tag

❓ FAQ (Frequently Asked Questions)

❓ Apa perbedaan antara MySQLi dan PDO untuk menampilkan data dari database PHP?

MySQLi hanya mendukung MySQL database, sedangkan PDO mendukung multiple database (MySQL, PostgreSQL, SQLite, dll). PDO lebih fleksibel dan memiliki syntax yang konsisten. MySQLi sedikit lebih cepat untuk MySQL, tapi PDO lebih portable dan recommended untuk project baru.

❓ Bagaimana cara mencegah SQL Injection saat menampilkan data dari database?

Gunakan prepared statements dengan parameter binding. Jangan pernah langsung concatenate user input ke query SQL. Contoh: gunakan $stmt->bindParam() atau placeholder (?) dalam query. Selalu validasi dan sanitize input user sebelum digunakan dalam query.

❓ Bagaimana cara menampilkan data dalam jumlah besar tanpa membuat website lambat?

Implementasikan pagination dengan LIMIT dan OFFSET. Gunakan AJAX untuk loading data secara asynchronous. Buat index pada kolom yang sering di-query. Consider menggunakan lazy loading atau infinite scroll untuk user experience yang lebih baik.

❓ Apakah lebih baik menggunakan fetch() atau fetchAll() untuk menampilkan data?

fetch() untuk single record atau saat memproses data satu per satu dalam loop (memory efficient). fetchAll() untuk multiple records yang akan diproses sekaligus (lebih cepat tapi konsumsi memory lebih besar). Pilih sesuai kebutuhan dan ukuran data.

❓ Bagaimana cara menangani error saat koneksi database gagal?

Gunakan try-catch block untuk menangkap PDOException atau mysqli_connect_error(). Jangan tampilkan error message langsung ke user. Log error ke file dan tampilkan pesan generic ke user. Implement retry mechanism dan fallback options jika memungkinkan.

❓ Apakah perlu menutup koneksi database secara manual di PHP?

PHP secara otomatis menutup koneksi saat script selesai, tapi best practice adalah menutup koneksi secara manual dengan $conn->close() (MySQLi) atau $conn = null (PDO). Ini membantu free up resources lebih cepat, terutama untuk aplikasi dengan traffic tinggi.

❓ Bagaimana cara mengoptimalkan query untuk menampilkan data yang lebih cepat?

Buat index pada kolom yang sering digunakan dalam WHERE clause. Gunakan EXPLAIN untuk analyze query performance. Hindari SELECT *, pilih kolom yang diperlukan saja. Gunakan JOIN yang efisien dan consider denormalization untuk query yang kompleks. Implement query caching jika memungkinkan.

❓ Apakah bisa menampilkan data dari multiple database dalam satu aplikasi PHP?

Ya, bisa. Buat multiple connection objects untuk setiap database. Dengan PDO, Anda bisa connect ke berbagai jenis database (MySQL, PostgreSQL, SQLite) dalam satu aplikasi. Pastikan manage connections dengan baik dan close yang tidak digunakan untuk menghemat resources.

🎓 Kesimpulan

Selamat! Anda telah berhasil mempelajari menampilkan data dari database PHP dengan lengkap dan komprehensif. Kemampuan untuk mengkoneksikan PHP dengan database MySQL dan menampilkan data secara dinamis adalah skill fundamental yang akan sangat berguna dalam career web development Anda.

Dari tutorial ini, Anda telah menguasai:

  • Database Setup dan konfigurasi MySQL dengan PHP
  • Koneksi Database menggunakan MySQLi dan PDO
  • Query Execution dengan prepared statements untuk keamanan
  • Data Display dalam berbagai format (table, cards, detail view)
  • Pagination Implementation untuk handling large datasets
  • Search Functionality dengan LIKE queries
  • Error Handling dan debugging techniques
  • Security Best Practices untuk mencegah SQL injection
  • Performance Optimization untuk aplikasi yang scalable

Dengan menguasai teknik-teknik ini, Anda dapat membangun aplikasi web yang powerful dengan kemampuan mengelola dan menampilkan data secara efisien. Terus kembangkan dengan menambahkan fitur-fitur advanced seperti real-time updates, data visualization, dan API integration!

🚀 Ready to Build Dynamic Web Applications?

Mulai journey database programming Anda dengan PHP dan MySQL!

💾 Database Mastery
🔒 Secure Coding
⚡ High Performance

⚡ JavaScript untuk Interaktivitas

Tambahkan JavaScript untuk meningkatkan user experience:

📁 File: public/assets/js/custom.js

// CodeIgniter Bootstrap App JavaScript
document.addEventListener('DOMContentLoaded', function() {
    
    // Initialize tooltips
    var tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]'));
    var tooltipList = tooltipTriggerList.map(function (tooltipTriggerEl) {
        return new bootstrap.Tooltip(tooltipTriggerEl);
    });

    // Initialize popovers
    var popoverTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="popover"]'));
    var popoverList = popoverTriggerList.map(function (popoverTriggerEl) {
        return new bootstrap.Popover(popoverTriggerEl);
    });

    // Auto-hide alerts after 5 seconds
    const alerts = document.querySelectorAll('.alert');
    alerts.forEach(alert => {
        setTimeout(() => {
            if (alert && alert.classList.contains('show')) {
                const bsAlert = new bootstrap.Alert(alert);
                bsAlert.close();
            }
        }, 5000);
    });

    // Form validation enhancement
    const forms = document.querySelectorAll('form[novalidate]');
    forms.forEach(form => {
        form.addEventListener('submit', function(event) {
            if (!form.checkValidity()) {
                event.preventDefault();
                event.stopPropagation();
                
                // Focus on first invalid field
                const firstInvalid = form.querySelector(':invalid');
                if (firstInvalid) {
                    firstInvalid.focus();
                }
            }
            form.classList.add('was-validated');
        });

        // Real-time validation
        const inputs = form.querySelectorAll('input, select, textarea');
        inputs.forEach(input => {
            input.addEventListener('blur', function() {
                if (this.checkValidity()) {
                    this.classList.remove('is-invalid');
                    this.classList.add('is-valid');
                } else {
                    this.classList.remove('is-valid');
                    this.classList.add('is-invalid');
                }
            });
        });
    });

    // Smooth scrolling for anchor links
    document.querySelectorAll('a[href^="#"]').forEach(anchor => {
        anchor.addEventListener('click', function (e) {
            e.preventDefault();
            const target = document.querySelector(this.getAttribute('href'));
            if (target) {
                target.scrollIntoView({
                    behavior: 'smooth',
                    block: 'start'
                });
            }
        });
    });

    // Scroll to top button
    createScrollToTopButton();

    // Loading states for buttons
    const submitButtons = document.querySelectorAll('button[type="submit"]');
    submitButtons.forEach(button => {
        button.addEventListener('click', function() {
            const form = this.closest('form');
            if (form && form.checkValidity()) {
                showLoadingState(this);
            }
        });
    });

    // Navbar scroll effect
    window.addEventListener('scroll', function() {
        const navbar = document.querySelector('.navbar');
        if (window.scrollY > 50) {
            navbar.classList.add('navbar-scrolled');
        } else {
            navbar.classList.remove('navbar-scrolled');
        }
    });

    // Animation on scroll
    const observerOptions = {
        threshold: 0.1,
        rootMargin: '0px 0px -50px 0px'
    };

    const observer = new IntersectionObserver((entries) => {
        entries.forEach(entry => {
            if (entry.isIntersecting) {
                entry.target.classList.add('fade-in');
                observer.unobserve(entry.target);
            }
        });
    }, observerOptions);

    // Observe elements for animation
    const animateElements = document.querySelectorAll('.card, .feature-card, .alert');
    animateElements.forEach(el => {
        observer.observe(el);
    });

});

// Create scroll to top button
function createScrollToTopButton() {
    const scrollButton = document.createElement('button');
    scrollButton.className = 'scroll-to-top';
    scrollButton.innerHTML = '';
    scrollButton.setAttribute('title', 'Scroll to Top');
    document.body.appendChild(scrollButton);

    // Show/hide button based on scroll position
    window.addEventListener('scroll', function() {
        if (window.pageYOffset > 300) {
            scrollButton.classList.add('show');
        } else {
            scrollButton.classList.remove('show');
        }
    });

    // Scroll to top when clicked
    scrollButton.addEventListener('click', function() {
        window.scrollTo({
            top: 0,
            behavior: 'smooth'
        });
    });
}

// Show loading state for buttons
function showLoadingState(button) {
    const originalText = button.innerHTML;
    button.innerHTML = ' Processing...';
    button.disabled = true;

    // Reset button after 3 seconds (adjust as needed)
    setTimeout(() => {
        button.innerHTML = originalText;
        button.disabled = false;
    }, 3000);
}

// Toast notification function
function showToast(message, type = 'info', duration = 5000) {
    const toastContainer = getOrCreateToastContainer();
    
    const toastId = 'toast-' + Date.now();
    const toast = document.createElement('div');
    toast.id = toastId;
    toast.className = `toast align-items-center text-white bg-${type} border-0`;
    toast.setAttribute('role', 'alert');
    toast.innerHTML = `

 

${message}

 

`; toastContainer.appendChild(toast); const bsToast = new bootstrap.Toast(toast, { delay: duration }); bsToast.show(); // Remove toast element after it’s hidden toast.addEventListener(‘hidden.bs.toast’, () => { toast.remove(); }); } // Get or create toast container function getOrCreateToastContainer() { let container = document.querySelector(‘.toast-container’); if (!container) { container = document.createElement(‘div’); container.className = ‘toast-container position-fixed bottom-0 end-0 p-3’; container.style.zIndex = ‘1055’; document.body.appendChild(container); } return container; } // Get appropriate icon for toast type function getToastIcon(type) { const icons = { ‘success’: ‘check-circle’, ‘danger’: ‘exclamation-triangle’, ‘warning’: ‘exclamation-circle’, ‘info’: ‘info-circle’, ‘primary’: ‘info-circle’ }; return icons[type] || ‘info-circle’; } // Utility function to format currency function formatCurrency(amount, currency = ‘IDR’) { return new Intl.NumberFormat(‘id-ID’, { style: ‘currency’, currency: currency, minimumFractionDigits: 0 }).format(amount); } // Utility function to format date function formatDate(date, options = {}) { const defaultOptions = { year: ‘numeric’, month: ‘long’, day: ‘numeric’ }; const formatOptions = { …defaultOptions, …options }; return new Intl.DateTimeFormat(‘id-ID’, formatOptions).format(new Date(date)); } // Debounce function for search inputs function debounce(func, wait, immediate) { var timeout; return function() { var context = this, args = arguments; var later = function() { timeout = null; if (!immediate) func.apply(context, args); }; var callNow = immediate && !timeout; clearTimeout(timeout); timeout = setTimeout(later, wait); if (callNow) func.apply(context, args); }; }

🔧 Konfigurasi Routes

Update file routes untuk menangani URL yang user-friendly:

📁 File: app/Config/Routes.php

<?php

use CodeIgniter\Router\RouteCollection;

/**
 * @var RouteCollection $routes
 */

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

// Static pages
$routes->get('about', 'Home::about');
$routes->match(['get', 'post'], 'contact', 'Home::contact');

// API routes (if needed)
$routes->group('api', function($routes) {
    $routes->get('users', 'Api\Users::index');
    $routes->post('contact', 'Api\Contact::send');
});

// Admin routes (if needed)
$routes->group('admin', ['filter' => 'auth'], function($routes) {
    $routes->get('dashboard', 'Admin\Dashboard::index');
    $routes->resource('users', ['controller' => 'Admin\Users']);
});

🔒 Security dan Best Practices

🛡️ Keamanan Aplikasi

1. CSRF Protection
CodeIgniter 4 memiliki built-in CSRF protection. Aktifkan di app/Config/Filters.php
2. XSS Prevention
Gunakan esc() function untuk output data user:
3. Input Validation
Selalu validasi input menggunakan CodeIgniter Validation library
4. Environment Configuration
Set CI_ENVIRONMENT=production dan disable debug mode untuk production
5. Asset Security
Gunakan HTTPS untuk CDN dan implement Content Security Policy (CSP)

⚡ Performance Optimization

🚀 Tips Optimisasi

📦 Asset Optimization

  • Minification: Minify CSS dan JavaScript files untuk production
  • Compression: Enable GZIP compression di server
  • Caching: Set proper cache headers untuk static assets
  • CDN: Gunakan CDN untuk Bootstrap dan library eksternal

🗄️ Database Optimization

  • Query Optimization: Gunakan CodeIgniter Query Builder dengan efisien
  • Caching: Implement database query caching
  • Indexing: Tambahkan database indexes pada kolom yang sering di-query
  • Connection Pooling: Optimize database connections

🔗 Artikel Terkait

Untuk memperdalam pemahaman CodeIgniter dan Bootstrap, baca juga artikel-artikel berikut:

🛠️ Troubleshooting Common Issues

❌ Masalah Umum dan Solusinya

Problem: Bootstrap CSS tidak load
Solution: Periksa path file, pastikan base_url() sudah benar, dan cek network tab di browser developer tools
Problem: JavaScript components tidak berfungsi
Solution: Pastikan Bootstrap JS dimuat setelah jQuery (jika menggunakan), dan cek console untuk error
Problem: Responsive design tidak bekerja
Solution: Tambahkan meta viewport tag dan pastikan menggunakan Bootstrap grid system dengan benar
Problem: Custom CSS tidak override Bootstrap
Solution: Load custom CSS setelah Bootstrap CSS dan gunakan specificity yang tepat

❓ FAQ (Frequently Asked Questions)

❓ Apakah wajib menggunakan Bootstrap dengan CodeIgniter?

Tidak wajib. CodeIgniter adalah backend framework yang tidak terikat dengan frontend framework tertentu. Anda bisa menggunakan Tailwind CSS, Bulma, Foundation, atau bahkan custom CSS. Bootstrap hanya mempermudah development dengan komponen UI yang sudah jadi.

❓ Bagaimana cara menggunakan Bootstrap dengan CodeIgniter 3?

Prinsipnya sama, tapi struktur folder sedikit berbeda. Di CI3, letakkan assets di folder assets/ di root project, bukan di public/. Gunakan base_url(‘assets/…’) untuk memanggil file CSS dan JS Bootstrap.

❓ Apakah lebih baik menggunakan CDN atau file lokal untuk Bootstrap?

CDN lebih cepat untuk development dan mengurangi bandwidth server. File lokal lebih baik untuk production karena tidak bergantung pada koneksi eksternal dan bisa dikustomisasi. Untuk production, gunakan file lokal yang sudah diminify.

❓ Bagaimana cara mengkustomisasi tema Bootstrap di CodeIgniter?

Buat file custom.css yang dimuat setelah Bootstrap CSS. Override variabel CSS Bootstrap atau gunakan SASS untuk compile Bootstrap dengan custom variables. Anda juga bisa menggunakan Bootstrap theme builder online.

❓ Apakah Bootstrap 5 kompatibel dengan semua browser?

Bootstrap 5 mendukung browser modern (Chrome, Firefox, Safari, Edge). Untuk IE11 dan browser lama, gunakan Bootstrap 4. Selalu cek browser compatibility di dokumentasi resmi Bootstrap.

❓ Bagaimana cara mengatasi konflik CSS antara Bootstrap dan custom styles?

Gunakan CSS specificity yang lebih tinggi, atau gunakan !important dengan hati-hati. Lebih baik override dengan class yang lebih spesifik seperti .my-custom-class .btn daripada langsung .btn. Gunakan CSS modules atau scoped styles jika memungkinkan.

❓ Apakah bisa menggunakan multiple CSS frameworks dalam satu project CodeIgniter?

Secara teknis bisa, tapi tidak direkomendasikan karena akan menyebabkan konflik CSS dan ukuran file yang besar. Lebih baik pilih satu framework utama (Bootstrap) dan tambahkan custom CSS untuk kebutuhan spesifik.

❓ Bagaimana cara mengoptimalkan performa aplikasi CodeIgniter dengan Bootstrap?

Gunakan only komponen Bootstrap yang diperlukan, minify CSS/JS files, enable GZIP compression, gunakan CDN, implement browser caching, dan optimize images. Untuk production, consider menggunakan build tools seperti Webpack atau Gulp.

🎓 Kesimpulan

Selamat! Anda telah berhasil mempelajari cara menggunakan Bootstrap di CodeIgniter dengan lengkap dan komprehensif. Kombinasi antara CodeIgniter sebagai backend framework yang powerful dan Bootstrap sebagai frontend framework yang responsive akan membantu Anda membangun aplikasi web yang modern, functional, dan user-friendly.

Dari tutorial ini, Anda telah menguasai:

  • Setup dan Konfigurasi Bootstrap di CodeIgniter 4
  • Asset Management yang optimal dan terstruktur
  • MVC Architecture dengan Bootstrap integration
  • Responsive Design menggunakan Bootstrap grid system
  • Form Handling dengan Bootstrap styling dan validation
  • Custom CSS dan JavaScript integration
  • Performance Optimization dan security best practices
  • Troubleshooting common issues

Aplikasi yang Anda bangun dengan CodeIgniter dan Bootstrap akan memiliki performa yang baik, tampilan yang menarik, dan user experience yang optimal. Terus kembangkan dengan menambahkan fitur-fitur advanced seperti AJAX integration, real-time notifications, dan progressive web app (PWA) features!

🚀 Ready to Build Amazing Web Applications?

Mulai journey web development Anda dengan CodeIgniter dan Bootstrap!

💻 Modern Framework
🎯 Responsive Design
⚡ Fast Development

Leave a Reply

Your email address will not be published. Required fields are marked *