Project PHP MySQL: Aplikasi CRUD dengan Upload Image

Project PHP MySQL: Aplikasi CRUD dengan Upload Image

Project PHP MySQL: Membuat Aplikasi CRUD dengan Upload & Preview Gambar Menggunakan Bootstrap 5

PHP dan MySQL adalah kombinasi powerful untuk membangun aplikasi web dinamis. Dalam tutorial ini, kita akan membuat aplikasi CRUD (Create, Read, Update, Delete) dengan fitur upload dan preview gambar menggunakan Bootstrap 5 untuk tampilan yang modern dan responsif. Project ini sangat cocok untuk pemula yang ingin mempelajari pengembangan web full-stack.

Fitur Utama Aplikasi

✅ Upload Gambar – Menyimpan gambar ke server & database
✅ Preview Gambar – Menampilkan daftar gambar dalam bentuk grid
✅ Operasi CRUD Lengkap
✅ Tampilan Modern dengan Bootstrap 5
✅ Validasi File (hanya menerima JPG, JPEG, PNG, GIF)

Persiapan Project

1. Struktur Folder

php-crud-upload/
├── assets/
│   ├── css/
│   │   └── style.css
│   └── images/
├── config/
│   └── database.php
├── uploads/
├── create.php
├── read.php
├── update.php
├── delete.php
└── index.php

2. Setup Database

Buat database crud_upload dan tabel images:

sql
CREATE DATABASE crud_upload;

USE crud_upload;

CREATE TABLE images (
    id INT AUTO_INCREMENT PRIMARY KEY,
    title VARCHAR(255) NOT NULL,
    filename VARCHAR(255) NOT NULL,
    uploaded_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

Implementasi Kode

1. Konfigurasi Database (config/database.php)

php
<?php
$host = "localhost";
$user = "root";
$pass = "";
$dbname = "crud_upload";

try {
    $conn = new PDO("mysql:host=$host;dbname=$dbname", $user, $pass);
    $conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} catch(PDOException $e) {
    die("Koneksi gagal: " . $e->getMessage());
}
?>

2. Tampilan Utama (index.php)

php
<?php 
include 'config/database.php';
$stmt = $conn->query("SELECT * FROM images ORDER BY uploaded_at DESC");
$images = $stmt->fetchAll();
?>

<!DOCTYPE html>
<html lang="id">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>CRUD Upload Gambar</title>
    <!-- Bootstrap 5 CSS -->
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
    <!-- Font Awesome -->
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
    <style>
        .card-img-top {
            height: 200px;
            object-fit: cover;
        }
    </style>
</head>
<body class="bg-light">
    <div class="container py-5">
        <div class="row">
            <div class="col-md-8 mx-auto">
                <div class="card shadow">
                    <div class="card-header bg-primary text-white">
                        <h3 class="text-center">
                            <i class="fas fa-image me-2"></i>CRUD Upload Gambar
                        </h3>
                    </div>
                    
                    <!-- Form Upload -->
                    <div class="card-body">
                        <form action="create.php" method="post" enctype="multipart/form-data" class="mb-4">
                            <div class="row g-3">
                                <div class="col-md-6">
                                    <input type="text" name="title" class="form-control" placeholder="Judul Gambar" required>
                                </div>
                                <div class="col-md-4">
                                    <input type="file" name="image" class="form-control" accept="image/*" required>
                                </div>
                                <div class="col-md-2">
                                    <button type="submit" name="upload" class="btn btn-primary w-100">
                                        <i class="fas fa-upload me-1"></i> Upload
                                    </button>
                                </div>
                            </div>
                        </form>

                        <!-- Daftar Gambar -->
                        <div class="row row-cols-1 row-cols-md-3 g-4">
                            <?php foreach($images as $image): ?>
                            <div class="col">
                                <div class="card h-100 shadow-sm">
                                    <img src="uploads/<?= $image['filename'] ?>" class="card-img-top" alt="<?= $image['title'] ?>">
                                    <div class="card-body">
                                        <h5 class="card-title"><?= $image['title'] ?></h5>
                                        <small class="text-muted">
                                            <?= date('d M Y H:i', strtotime($image['uploaded_at'])) ?>
                                        </small>
                                    </div>
                                    <div class="card-footer bg-white">
                                        <a href="update.php?id=<?= $image['id'] ?>" class="btn btn-sm btn-warning">
                                            <i class="fas fa-edit"></i>
                                        </a>
                                        <a href="delete.php?id=<?= $image['id'] ?>" 
                                           class="btn btn-sm btn-danger" 
                                           onclick="return confirm('Hapus gambar ini?')">
                                            <i class="fas fa-trash"></i>
                                        </a>
                                    </div>
                                </div>
                            </div>
                            <?php endforeach; ?>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>

    <!-- Bootstrap 5 JS -->
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>

3. Proses Upload (create.php)

php
<?php
include 'config/database.php';

if(isset($_POST['upload'])) {
    $title = $_POST['title'];
    $targetDir = "uploads/";
    $fileName = uniqid() . '_' . basename($_FILES["image"]["name"]);
    $targetFilePath = $targetDir . $fileName;
    $fileType = strtolower(pathinfo($targetFilePath, PATHINFO_EXTENSION));

    // Validasi
    $allowedTypes = ["jpg", "jpeg", "png", "gif"];
    if(in_array($fileType, $allowedTypes)) {
        if(move_uploaded_file($_FILES["image"]["tmp_name"], $targetFilePath)) {
            $stmt = $conn->prepare("INSERT INTO images (title, filename) VALUES (?, ?)");
            $stmt->execute([$title, $fileName]);
            $_SESSION['success'] = "Gambar berhasil diupload!";
        } else {
            $_SESSION['error'] = "Gagal mengupload gambar.";
        }
    } else {
        $_SESSION['error'] = "Hanya format JPG, JPEG, PNG & GIF yang diperbolehkan.";
    }
    header("Location: index.php");
    exit();
}
?>

4. Update Data (update.php)

php
<?php 
session_start();
include 'config/database.php';

// Check if ID exists
if(!isset($_GET['id'])) {
    $_SESSION['error'] = "ID gambar tidak valid";
    header("Location: index.php");
    exit();
}

$id = $_GET['id'];

try {
    $stmt = $conn->prepare("SELECT * FROM images WHERE id=?");
    $stmt->execute([$id]);
    $image = $stmt->fetch();

    if(!$image) {
        $_SESSION['error'] = "Gambar tidak ditemukan";
        header("Location: index.php");
        exit();
    }
} catch(PDOException $e) {
    $_SESSION['error'] = "Error: " . $e->getMessage();
    header("Location: index.php");
    exit();
}

if(isset($_POST['update'])) {
    $title = $_POST['title'];
    $updateData = [':title' => $title, ':id' => $id];
    $success = false;

    // Handle file upload if new image is provided
    if(!empty($_FILES["image"]["name"])) {
        $targetDir = "uploads/";
        $fileName = uniqid() . '_' . basename($_FILES["image"]["name"]);
        $targetFilePath = $targetDir . $fileName;
        $fileType = strtolower(pathinfo($targetFilePath, PATHINFO_EXTENSION));

        // Validate file type
        $allowedTypes = ["jpg", "jpeg", "png", "gif"];
        if(in_array($fileType, $allowedTypes)) {
            // Upload new file
            if(move_uploaded_file($_FILES["image"]["tmp_name"], $targetFilePath)) {
                // Delete old file
                if(file_exists("uploads/" . $image['filename'])) {
                    unlink("uploads/" . $image['filename']);
                }
                $updateData[':filename'] = $fileName;
                $success = true;
            } else {
                $_SESSION['error'] = "Gagal mengupload gambar baru";
            }
        } else {
            $_SESSION['error'] = "Hanya format JPG, JPEG, PNG & GIF yang diperbolehkan";
        }
    } else {
        $success = true;
    }

    // Update database if validation passed
    if($success) {
        try {
            $sql = "UPDATE images SET title=:title" . 
                   (isset($updateData[':filename']) ? ", filename=:filename" : "") . 
                   " WHERE id=:id";
            
            $stmt = $conn->prepare($sql);
            $stmt->execute($updateData);
            
            $_SESSION['success'] = "Data berhasil diupdate!";
            header("Location: index.php");
            exit();
        } catch(PDOException $e) {
            $_SESSION['error'] = "Error database: " . $e->getMessage();
        }
    }
}
?>

<!DOCTYPE html>
<html lang="id">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Edit Gambar - Azroi Tech</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
    <style>
        .preview-image {
            max-height: 300px;
            object-fit: contain;
            margin-bottom: 20px;
            border-radius: 5px;
        }
    </style>
</head>
<body class="bg-light">
    <div class="container py-5">
        <div class="row justify-content-center">
            <div class="col-md-8">
                <div class="card shadow">
                    <div class="card-header bg-primary text-white">
                        <h3 class="text-center">
                            <i class="fas fa-edit me-2"></i>Edit Gambar
                        </h3>
                    </div>
                    
                    <div class="card-body">
                        <!-- Error/Success Messages -->
                        <?php if(isset($_SESSION['error'])): ?>
                            <div class="alert alert-danger">
                                <?= $_SESSION['error']; unset($_SESSION['error']); ?>
                            </div>
                        <?php endif; ?>
                        
                        <?php if(isset($_SESSION['success'])): ?>
                            <div class="alert alert-success">
                                <?= $_SESSION['success']; unset($_SESSION['success']); ?>
                            </div>
                        <?php endif; ?>

                        <!-- Current Image Preview -->
                        <div class="text-center">
                            <img src="uploads/<?= htmlspecialchars($image['filename']) ?>" 
                                 alt="<?= htmlspecialchars($image['title']) ?>" 
                                 class="preview-image img-thumbnail">
                            <p class="text-muted">Gambar saat ini</p>
                        </div>

                        <!-- Edit Form -->
                        <form action="update.php?id=<?= $id ?>" method="post" enctype="multipart/form-data">
                            <div class="mb-3">
                                <label for="title" class="form-label">Judul Gambar</label>
                                <input type="text" class="form-control" id="title" name="title" 
                                       value="<?= htmlspecialchars($image['title']) ?>" required>
                            </div>
                            
                            <div class="mb-3">
                                <label for="image" class="form-label">Gambar Baru (Opsional)</label>
                                <input class="form-control" type="file" id="image" name="image"
                                       accept="image/jpeg, image/png, image/gif">
                                <div class="form-text">
                                    Kosongkan jika tidak ingin mengubah gambar. Format: JPG, PNG, GIF (Max 2MB)
                                </div>
                            </div>
                            
                            <div class="d-grid gap-2 d-md-flex justify-content-md-end">
                                <a href="index.php" class="btn btn-secondary me-md-2">
                                    <i class="fas fa-arrow-left me-1"></i> Kembali
                                </a>
                                <button type="submit" name="update" class="btn btn-primary">
                                    <i class="fas fa-save me-1"></i> Simpan Perubahan
                                </button>
                            </div>
                        </form>
                    </div>
                </div>
            </div>
        </div>
    </div>

    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>

Keunggulan Project Ini

  1. Responsive Design – Tampilan optimal di semua perangkat
  2. Modern UI – Menggunakan Bootstrap 5 dan Font Awesome
  3. Keamanan:
    • Nama file unik dengan uniqid()
    • Validasi ekstensi file
    • PDO untuk mencegah SQL injection
  4. User Experience:
    • Notifikasi sukses/gagal
    • Konfirmasi sebelum menghapus
    • Preview gambar langsung


Tips Pengembangan:

  • Tambahkan pagination untuk data yang banyak
  • Implementasi AJAX untuk operasi CRUD
  • Tambahkan fitur multi-upload

Dengan menyelesaikan project ini, Anda telah mempelajari:
✔ Konsep CRUD dengan PHP & MySQL
✔ Upload dan manajemen file
✔ Integrasi Bootstrap 5

Comments

No comments yet. Why don’t you start the discussion?

Leave a Reply

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