Buku Panduan Teknis PixelGallery
Dokumentasi ringkas arsitektur aplikasi pengunggahan dan galeri gambar berbasis PHP 8, basis data PostgreSQL Supabase cloud, serta penanganan berkas serverless di Vercel.
Ringkasan Aplikasi
PixelGallery adalah implementasi tugas Jobsheet 08 (Full-Stack CRUD & Basis Data Relasional). Aplikasi ini berfokus pada alur lengkap pengelolaan gambar: pengguna dapat mengunggah file foto (JPG, PNG, WEBP), memberi metadata (judul, pengunggah, kategori, tahun, deskripsi), menjelajahi galeri dalam kartu responsif, mencari berdasarkan kata kunci, serta menghapus data.
$_FILES, dan merancang sistem yang kompatibel dengan arsitektur cloud serverless.
Alur Data & Arsitektur Sistem
Aplikasi ini mengadopsi alur data End-to-End yang memisahkan logika antarmuka, validasi server, penyimpanan persisten cloud, dan penyajian data biner secara efisien:
Pengguna Mengisi Form & Memilih Gambar
Data teks dan file biner dikirim melalui HTTP POST dengan tipe payload multipart.
Validasi Sisi Server & Konversi Base64 (upload-asset.php)
PHP memvalidasi status upload, whitelist ekstensi (JPG/PNG/WEBP), dan batas ukuran 5MB. Berkas di-encode ke Base64 Data URL agar aman di lingkungan serverless yang bersifat read-only.
Penyimpanan Persisten ke Cloud Supabase PostgreSQL
Query INSERT dieksekusi menggunakan Prepared Statement PDO ke tabel galeri di Supabase Seoul Pooler (port 6543).
Penyajian Gambar Teroptimasi (gambar.php)
Endpoint khusus biner mengalirkan data gambar langsung dengan header Content-Type dan HTTP Browser Cache sehingga ukuran dokumen HTML halaman katalog tetap ringan (< 10 KB).
1. Struktur HTML & CSS
A. Form Upload Berkas (upload-asset.php)
Secara default, form HTML mengirim data dengan encoding teks biasa. Untuk mengirimkan file gambar, form wajib menyertakan atribut enctype="multipart/form-data". Tanpa atribut ini, berkas tidak akan masuk ke array $_FILES di server.
<!-- Form pengunggahan gambar -->
<form action="upload-asset.php" method="POST" enctype="multipart/form-data">
<input type="text" name="judul" placeholder="Judul foto" required>
<input type="text" name="pengunggah" placeholder="Nama Anda" required>
<select name="kategori" required>
<option value="Wallpaper">Wallpaper</option>
<option value="Pemandangan">Pemandangan & Alam</option>
<option value="Fotografi">Fotografi Umum</option>
</select>
<input type="file" name="gambar" accept="image/png, image/jpeg, image/webp" required>
<button type="submit">Unggah Gambar</button>
</form>
B. Grid Galeri Responsif (CSS Grid)
Koleksi gambar disusun menggunakan CSS Grid modern dengan fungsi bawaan repeat(auto-fill, minmax(280px, 1fr)). Pendekatan ini membuat galeri otomatis responsif di semua ukuran layar (desktop 3-4 kolom, tablet 2 kolom, dan smartphone 1 kolom) tanpa library eksternal.
.gallery-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
gap: 1.5rem;
}
C. Modular Header & Footer Layout
Struktur halaman menerapkan prinsip modular DRY (Don't Repeat Yourself). Navigasi atas diletakkan di includes/header.php dan bagian bawah di includes/footer.php.
Variabel dinamis $base secara otomatis menghitung kedalaman direktori (../) menggunakan fungsi dirname() dan substr_count(), sehingga aset CSS/JS dan tautan navigasi tidak pernah rusak saat diakses dari subfolder lokal maupun root domain Vercel.
2. Logika Backend PHP & Basis Data
A. Koneksi Database PDO (includes/koneksi.php)
Koneksi database dibuat menggunakan PHP Data Objects (PDO) dengan driver pgsql. Koneksi dirancang memiliki failover: memprioritaskan cloud PostgreSQL Supabase (port 6543 pooler IPv4), dan jika dijalankan di komputer lokal tanpa internet otomatis mencoba PostgreSQL lokal (port 5433 / 5432).
$dsn = "pgsql:host={$host};port={$port};dbname={$db};sslmode=require";
$pdo = new PDO($dsn, $user, $pass, [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_EMULATE_PREPARES => false,
]);
B. Penanganan Upload File & Base64 Serverless (upload-asset.php)
Sebelum berkas disimpan, PHP menjalankan serangkaian validasi ketat:
- Status Unggah: Memeriksa
$_FILES['gambar']['error'] === UPLOAD_ERR_OK. - Ekstensi Berkas: Mengambil ekstensi menggunakan
pathinfo($nama, PATHINFO_EXTENSION)dan mencocokkannya dengan daftar izin (jpg, jpeg, png, webp, gif). - Batas Ukuran: Memastikan
$_FILES['gambar']['size'] <= 5MB.
file_get_contents(), dikonversi menjadi format Base64 Data URL, dan disimpan persisten di kolom file_gambar (tipe TEXT) di PostgreSQL Supabase cloud.
// 1. Baca isi berkas dan encode ke Base64 Data URL
$konten = file_get_contents($_FILES['gambar']['tmp_name']);
$data_simpan = 'data:' . $tipe_mime . ';base64,' . base64_encode($konten);
// 2. Simpan menggunakan Prepared Statement anti-SQL Injection
$sql = "INSERT INTO galeri (judul, pengunggah, kategori, file_gambar, tahun, deskripsi)
VALUES (:judul, :pengunggah, :kategori, :file_gambar, :tahun, :deskripsi)";
$stmt = $pdo->prepare($sql);
$stmt->execute([
':judul' => $judul,
':pengunggah' => $pengunggah,
':kategori' => $kategori,
':file_gambar' => $data_simpan,
':tahun' => (int)$tahun,
':deskripsi' => $deskripsi
]);
C. Streaming Biner Teroptimasi (gambar.php)
Jika Base64 gambar disematkan langsung di dalam tag <img src="data:image/..."> pada halaman katalog, dokumen HTML akan berukuran hingga puluhan megabyte. Di Vercel, ini akan memicu error fatal 500 FUNCTION_RESPONSE_PAYLOAD_TOO_LARGE (batas respon maksimal 4.5MB).
Solusinya adalah membuat endpoint gambar.php?id=ID yang membaca data gambar dari database, mendekode Base64 kembali ke format biner murni, dan mengirimkannya ke browser dengan header HTTP yang tepat:
$id = (int)($_GET['id'] ?? 0);
$stmt = $pdo->prepare("SELECT file_gambar FROM galeri WHERE id = :id");
$stmt->execute([':id' => $id]);
$foto = $stmt->fetch(PDO::FETCH_ASSOC);
// Kirim HTTP Header & stream biner
header("Content-Type: " . $mime);
header("Cache-Control: public, max-age=86400"); // Cache browser 24 jam
echo base64_decode($biner_data);
exit;
D. Pencarian & Penghapusan Data (catalog.php)
Pencarian: Menggunakan klausa SQL ILIKE :q (PostgreSQL case-insensitive matching) untuk mencocokkan kata kunci pada judul, pengunggah, atau kategori tanpa membedakan huruf besar/kecil.
Penghapusan: Menerima parameter GET ?hapus=ID, divalidasi dengan filter_var(..., FILTER_VALIDATE_INT), lalu mengeksekusi DELETE FROM galeri WHERE id = :id.
E. Flash Message Session State
Pemberitahuan sukses atau gagal disimpan ke variabel sesi $_SESSION['flash'] sebelum melakukan HTTP Redirect (header("Location: ...")). Saat halaman tujuan dimuat, notifikasi dirender dan langsung dibersihkan dengan unset($_SESSION['flash']) agar pesan tidak muncul berulang saat halaman di-refresh.
B. Konfirmasi Hapus Data Interaktif
Tautan tombol hapus pada kartu galeri dilengkapi atribut inline:
onclick="return confirm('Apakah Anda yakin ingin menghapus gambar ini?');"
Jika pengguna menekan tombol Cancel, fungsi confirm() mengembalikan nilai false dan peramban membatalkan aksi navigasi URL sehingga data tidak terhapus tanpa sengaja.
C. Validasi Sisi Klien (app.js)
Event listener submit pada form upload memeriksa kelengkapan kolom wajib (judul, nama pengunggah, berkas foto) sebelum data dikirim ke server. Jika terdapat kolom kosong, form menampilkan pesan kesalahan secara instan di bawah input bersangkutan.
4. Cheat Sheet Tanya-Jawab Evaluasi Dosen
Gunakan poin-poin jawaban di bawah ini saat dosen menanyakan konsep teknis aplikasi:
1. Mengapa memilih PHP PDO daripada ekstensi MySQLi?
2. Bagaimana aplikasi ini mencegah serangan SQL Injection?
$sql = "SELECT ... " . $input). Kami menggunakan placeholder (:judul) melalui $pdo->prepare() dan $stmt->execute(). Mesin basis data memperlakukan nilai tersebut murni sebagai data, bukan perintah SQL yang dapat dieksekusi.
3. Bagaimana cara kerja upload file di serverless Vercel yang read-only?
file_gambar basis data cloud Supabase.
4. Mengapa dibuat file gambar.php terpisah dan tidak dicetak langsung di src?
gambar.php?id=ID, HTML tetap sangat kecil (< 10 KB), gambar dialirkan secara independen sebagai biner murni, dan browser dapat melakukan HTTP caching (Cache-Control: public, max-age=86400).
5. Apa fungsi htmlspecialchars() pada saat menampilkan data ke layar?
htmlspecialchars() menyaring karakter khusus HTML (seperti <, >, &, dan ") menjadi entitas karakter HTML yang aman (seperti < dan >). Hal ini mencegah eksekusi skrip berbahaya jika pengguna memasukkan tag <script> pada input judul atau pengunggah.