java php laravel linux mysql sql bootstrap html css query java php laravel linux mysql sql bootstrap html css query

Monday, October 12, 2026

login logout session php

🔐
Siapa yang Boleh Masuk?
Authentication: Registrasi User, Login, Logout, dan Session. Seri Pemrograman Web D4 MIK, Artikel 13 dari 16.
AuthenticationRegistrasi UserLogin PHPSessionLogout
± 12 menit
Estimasi baca
Lanjut
Level
2026
Tahun

Bayangkan kamu masuk ke rumah sakit tanpa ada satu pun petugas yang menanyakan identitasmu. Siapa saja bisa membuka rekam medis, mengubah diagnosis, bahkan menghapus data kunjungan. Ngeri, kan? Itulah yang terjadi pada aplikasi web tanpa sistem masuk. Di artikel ke-13 seri Pemrograman Web D4 MIK ini, kamu akan membangun login PHP session dari nol: registrasi user, login, proteksi halaman, sampai logout. Semuanya kamu praktikkan langsung di XAMPP dengan studi kasus klinik dan data pasien fiktif. Hasil akhirnya: hanya petugas yang punya akun yang bisa membuka halaman data pasien.

Artikel ini mengunci Sub-CPMK 9 (CPMK 4): kamu mampu menerapkan authentication, validasi, dan perlindungan dasar pada aplikasi web pengelolaan data kesehatan.

Login PHP Session: Cara Kerja Authentication di Balik Layar

Protokol HTTP itu pelupa. Setiap kali browser meminta halaman, server menganggapnya pengunjung baru. Jadi setelah kamu berhasil login di login.php, server tidak otomatis ingat kamu waktu kamu pindah ke dashboard.php. Di sinilah session bekerja.

Anggap saja loket pendaftaran rumah sakit. Setelah petugas memeriksa identitasmu, kamu dikasih gelang pasien bernomor unik. Di setiap ruangan, perawat cukup melihat gelang itu tanpa menanyakan KTP lagi. Dalam PHP, gelang itu adalah session ID yang disimpan browser dalam cookie bernama PHPSESSID. Catatan isinya (kamu siapa) disimpan di server, bukan di browser.

📐 Konsep Utama
Authentication = membuktikan siapa kamu (username + password).
Authorization = menentukan boleh apa kamu (dibahas di Artikel 14).

Alur login PHP session: verifikasi password → simpan penanda di $_SESSION → cek penanda itu di setiap halaman terlindungi.
BROWSER                          SERVER (PHP)
  |-- POST login.php ---------->|  cek username, password_verify()
  |<-- Set-Cookie: PHPSESSID ---|  $_SESSION["user_id"] = 5
  |-- GET dashboard.php ------->|  cookie dibawa, session dikenali
  |<-- halaman dashboard -------|
  |-- GET logout.php ---------->|  session_destroy()
  |<-- redirect ke login.php ---|
🔍 Analisis: Cookie vs Session
🍪 Cookie
Disimpan di browser. Pengguna bisa melihat dan mengubahnya. Cocok untuk hal ringan, misalnya preferensi tampilan. Jangan simpan data sensitif di sini.
🗄️ Session
Datanya disimpan di server. Browser hanya memegang ID-nya. Cocok untuk status login, nama petugas, dan penanda hak akses.
⚡ Insight Penting
Isi $_SESSION tidak pernah dikirim ke browser. Yang dikirim hanya session ID. Karena itu pengguna tidak bisa mengubah dirinya sendiri dari “petugas biasa” menjadi “admin” lewat browser. Tapi kalau ID itu dicuri, orang lain bisa menyamar jadi kamu. Makanya nanti kita pakai session_regenerate_id().

Praktik 1: Registrasi User untuk Login PHP Session

Sebelum ada yang bisa login, harus ada akun. Kita buat aplikasi mini “Klinik Sehat Sentosa” (nama fiktif). Ikuti langkahnya berurutan, ya.

1
Siapkan XAMPP dan folder kerja
Buka XAMPP Control Panel, klik Start pada Apache dan MySQL. Lalu buat folder klinik_auth di C:\xampp\htdocs\. Semua file di artikel ini disimpan di folder itu dengan Sublime Text.
2
Buat tabel users di phpMyAdmin
Buka http://localhost/phpmyadmin, pilih tab SQL, tempel kode berikut, lalu klik Kirim.
📄 query.sql
CREATE DATABASE IF NOT EXISTS db_klinik;
USE db_klinik;

CREATE TABLE users (
    id INT AUTO_INCREMENT PRIMARY KEY,
    nama_lengkap VARCHAR(100) NOT NULL,
    username VARCHAR(50) NOT NULL UNIQUE,
    password VARCHAR(255) NOT NULL,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
✅ Hasil yang diharapkan: tabel users muncul di database db_klinik dengan 5 kolom.
🔥 Fakta Menarik
Kolom password sengaja dibuat VARCHAR(255). Dokumentasi resmi PHP menyarankan panjang itu untuk hasil password_hash() karena algoritma bawaan bisa berganti seiring versi PHP. Kalau kolommu terlalu pendek, hash terpotong dan login akan selalu gagal.
3
Buat file koneksi.php
File ini dipanggil oleh semua halaman supaya koneksi database cukup ditulis sekali.
📄 koneksi.php
<?php
$koneksi = mysqli_connect("localhost", "root", "", "db_klinik");

if (!$koneksi) {
    die("Koneksi gagal: " . mysqli_connect_error());
}
mysqli_set_charset($koneksi, "utf8mb4");
4
Buat form registrasi (register.php)
Validasi dasar kita lakukan di sini: kolom tidak boleh kosong, password minimal 8 karakter, konfirmasi harus sama, dan username tidak boleh kembar. Password disimpan dalam bentuk hash, bukan teks asli.
📄 register.php
<?php
require "koneksi.php";
$pesan = "";

if ($_SERVER["REQUEST_METHOD"] === "POST") {
    $nama       = trim($_POST["nama_lengkap"]);
    $username   = trim($_POST["username"]);
    $password   = $_POST["password"];
    $konfirmasi = $_POST["konfirmasi"];

    if ($nama === "" || $username === "" || $password === "") {
        $pesan = "Semua kolom wajib diisi.";
    } elseif (strlen($password) < 8) {
        $pesan = "Password minimal 8 karakter.";
    } elseif ($password !== $konfirmasi) {
        $pesan = "Konfirmasi password tidak sama.";
    } else {
        $cek = mysqli_prepare($koneksi, "SELECT id FROM users WHERE username = ?");
        mysqli_stmt_bind_param($cek, "s", $username);
        mysqli_stmt_execute($cek);
        mysqli_stmt_store_result($cek);

        if (mysqli_stmt_num_rows($cek) > 0) {
            $pesan = "Username sudah dipakai.";
        } else {
            $hash   = password_hash($password, PASSWORD_DEFAULT);
            $simpan = mysqli_prepare($koneksi,
                "INSERT INTO users (nama_lengkap, username, password) VALUES (?, ?, ?)");
            mysqli_stmt_bind_param($simpan, "sss", $nama, $username, $hash);
            mysqli_stmt_execute($simpan);
            $pesan = "Registrasi berhasil! Silakan login.";
        }
    }
}
?>
<!DOCTYPE html>
<html lang="id">
<head><meta charset="UTF-8"><title>Registrasi Petugas</title></head>
<body>
    <h2>Registrasi Petugas Klinik</h2>
    <?php if ($pesan !== "") { echo "<p>" . htmlspecialchars($pesan) . "</p>"; } ?>
    <form method="post">
        <p>Nama lengkap<br><input type="text" name="nama_lengkap"></p>
        <p>Username<br><input type="text" name="username"></p>
        <p>Password<br><input type="password" name="password"></p>
        <p>Ulangi password<br><input type="password" name="konfirmasi"></p>
        <button type="submit">Daftar</button>
    </form>
    <p><a href="login.php">Sudah punya akun? Login</a></p>
</body>
</html>
✅ Hasil yang diharapkan: buka http://localhost/klinik_auth/register.php, daftarkan petugas fiktif (misalnya username petugas_ani). Di phpMyAdmin, kolom password berisi teks acak berawalan $2y$, bukan password aslinya.
💡 Tips
Perhatikan tanda ? pada query. Itu prepared statement: data dari form dikirim terpisah dari perintah SQL. Kebiasaan ini akan jadi tameng utama di Artikel 14 saat kita membahas SQL Injection.

Praktik 2: Login PHP Session dengan password_verify

Sekarang bagian serunya. Alurnya: ambil data user berdasarkan username, cocokkan password dengan password_verify(), lalu simpan penanda login di $_SESSION. Perhatikan juga pesan error yang sengaja dibuat sama untuk username salah maupun password salah, supaya penyerang tidak tahu username mana yang terdaftar.

5
Buat file login.php
Baris session_start() wajib berada paling atas, sebelum output apa pun.
📄 login.php
<?php
session_start();
require "koneksi.php";

if (isset($_SESSION["user_id"])) {
    header("Location: dashboard.php");
    exit;
}

$pesan = "";
if ($_SERVER["REQUEST_METHOD"] === "POST") {
    $username = trim($_POST["username"]);
    $password = $_POST["password"];

    $stmt = mysqli_prepare($koneksi,
        "SELECT id, nama_lengkap, password FROM users WHERE username = ?");
    mysqli_stmt_bind_param($stmt, "s", $username);
    mysqli_stmt_execute($stmt);
    $user = mysqli_fetch_assoc(mysqli_stmt_get_result($stmt));

    if ($user && password_verify($password, $user["password"])) {
        session_regenerate_id(true);
        $_SESSION["user_id"] = $user["id"];
        $_SESSION["nama"]    = $user["nama_lengkap"];
        header("Location: dashboard.php");
        exit;
    } else {
        $pesan = "Username atau password salah.";
    }
}
?>
<!DOCTYPE html>
<html lang="id">
<head><meta charset="UTF-8"><title>Login Petugas</title></head>
<body>
    <h2>Login Petugas Klinik</h2>
    <?php if ($pesan !== "") { echo "<p>" . htmlspecialchars($pesan) . "</p>"; } ?>
    <form method="post">
        <p>Username<br><input type="text" name="username"></p>
        <p>Password<br><input type="password" name="password"></p>
        <button type="submit">Masuk</button>
    </form>
    <p><a href="register.php">Belum punya akun? Daftar</a></p>
</body>
</html>
⚠️ Perhatian
Error headers already sent hampir selalu karena ada spasi, baris kosong, atau teks sebelum <?php. Fungsi session_start() dan header() hanya bisa jalan sebelum browser menerima output apa pun. Letakkan blok PHP di baris pertama file.
6
Uji coba login
Buka http://localhost/klinik_auth/login.php. Coba dua skenario: (a) password sengaja salah, (b) username dan password benar.
✅ Hasil yang diharapkan: skenario (a) menampilkan pesan “Username atau password salah.” Skenario (b) memindahkanmu ke dashboard.php (halamannya kita buat di langkah berikutnya, jadi sementara akan muncul error file tidak ditemukan; itu normal).

Praktik 3: Proteksi Halaman dan Logout pada Login PHP Session

Login saja belum cukup. Kalau halaman dashboard.php tidak mengecek session, siapa pun yang mengetik alamatnya langsung bisa masuk. Ibarat pintu depan dijaga satpam tapi pintu belakang terbuka lebar. Solusinya: satu file “penjaga” yang dipanggil di setiap halaman terlindungi.

7
Buat file penjaga (auth.php)
Kalau penanda login tidak ada, pengunjung langsung diusir ke halaman login.
📄 auth.php
<?php
session_start();

if (!isset($_SESSION["user_id"])) {
    header("Location: login.php");
    exit;
}

// cegah browser menyimpan halaman terlindungi di cache
header("Cache-Control: no-store, no-cache, must-revalidate");
8
Buat halaman dashboard.php
Cukup satu baris require "auth.php" di paling atas, maka halaman ini terkunci.
📄 dashboard.php
<?php require "auth.php"; ?>
<!DOCTYPE html>
<html lang="id">
<head><meta charset="UTF-8"><title>Dashboard Klinik</title></head>
<body>
    <h1>Dashboard Klinik Sehat Sentosa</h1>
    <p>Selamat datang, <?= htmlspecialchars($_SESSION["nama"]); ?>!</p>
    <p>Halaman ini hanya bisa dibuka petugas yang sudah login.</p>
    <a href="logout.php">Logout</a>
</body>
</html>
9
Buat logout.php
Logout yang benar punya tiga tahap: kosongkan data session, hapus cookie session ID, lalu hancurkan session di server.
📄 logout.php
<?php
session_start();

$_SESSION = [];

if (ini_get("session.use_cookies")) {
    $p = session_get_cookie_params();
    setcookie(session_name(), "", time() - 42000,
        $p["path"], $p["domain"], $p["secure"], $p["httponly"]);
}

session_destroy();
header("Location: login.php");
exit;
10
Uji tiga skenario
(1) Dalam keadaan belum login, ketik langsung http://localhost/klinik_auth/dashboard.php. (2) Login lalu buka dashboard. (3) Klik Logout, lalu tekan tombol Back di browser.
✅ Hasil yang diharapkan: skenario 1 otomatis dialihkan ke login.php; skenario 2 menampilkan “Selamat datang, nama petugas”; skenario 3 tidak lagi menampilkan isi dashboard.
💡 Tips
Mau melihat “gelang pasien”-mu? Tekan F12 di browser, buka tab Application (atau Storage) lalu Cookies. Kamu akan menemukan PHPSESSID. Hapus cookie itu secara manual, lalu refresh dashboard. Kamu langsung ditendang ke halaman login.

Ini rangkuman fungsi penting yang baru saja kamu pakai. Simpan sebagai contekan:

Fungsi / VariabelTugasnyaDipakai di
session_start()Memulai atau melanjutkan sessionlogin, auth, logout
$_SESSION[]Menyimpan data milik satu pengguna di serverlogin, dashboard
password_hash()Mengubah password jadi hash satu arahregister
password_verify()Mencocokkan password input dengan hashlogin
session_regenerate_id(true)Mengganti session ID setelah login berhasillogin
session_destroy()Menghapus data session di serverlogout

Error yang Sering Muncul & Cara Mengatasinya

Tenang, error itu teman belajar. Ini yang paling sering muncul saat mahasiswa praktik login PHP session di laboratorium:

Pesan / GejalaPenyebabSolusi
Cannot modify header information - headers already sentAda output (spasi, HTML, echo) sebelum session_start() atau header()Pindahkan blok PHP ke baris pertama. Di Sublime Text, simpan dengan File → Save with Encoding → UTF-8 (tanpa BOM).
Undefined array key "user_id"Mengakses $_SESSION sebelum session_start() atau sebelum loginPanggil session_start() dulu, dan cek dengan isset().
Login selalu gagal padahal password benarKolom password terlalu pendek, atau password di database diisi manual tanpa hashUbah jadi VARCHAR(255), lalu daftar ulang lewat register.php.
Duplicate entry ... for key 'username'Username sama sudah ada (kolom bersifat UNIQUE)Pakai username lain, atau pastikan pengecekan di register.php berjalan.
Failed opening required 'koneksi.php'Salah folder atau salah nama filePastikan semua file ada di satu folder htdocs\klinik_auth.
Setelah logout, tombol Back masih menampilkan dashboardBrowser menyimpan halaman di cachePastikan baris Cache-Control di auth.php ada, lalu refresh.

Latihan Mandiri: Kuatkan Authentication Klinikmu

Kerjakan di folder yang sama, lalu simpan tangkapan layar hasilnya sebagai bukti praktikum:

1
Catat waktu login terakhir. Tambahkan kolom last_login (DATETIME) di tabel users, isi dengan NOW() setiap login berhasil, dan tampilkan di dashboard.
2
Lindungi halaman data pasien. Buat daftar_pasien.php yang menampilkan 5 data pasien fiktif dari tabel pasien, dan kunci dengan auth.php. Buktikan halaman itu tidak bisa dibuka tanpa login.
3
Session timeout. Simpan waktu aktivitas terakhir di $_SESSION["waktu_terakhir"]. Jika selisihnya lebih dari 15 menit, otomatis logout. Ini praktik lazim untuk komputer bersama di loket pendaftaran.
📚 Referensi
🏁
Kesimpulan: Pintu Sudah Berpenjaga

Kamu sudah membangun siklus lengkap login PHP session: registrasi dengan password_hash(), login dengan password_verify(), penanda di $_SESSION, proteksi halaman lewat auth.php, dan logout yang membersihkan session sampai tuntas. Inti pelajarannya sederhana: browser hanya memegang “gelang”, sedangkan server yang menentukan siapa yang boleh masuk.

Tapi login baru satu lapis. Di Artikel 14, kita tambah lapisan berikutnya: hak akses per peran, hash password yang lebih dalam, serta perlindungan SQL Injection dan XSS. Bagian mana yang paling bikin kamu penasaran atau tersangkut? Ceritakan di kolom komentar, dan bagikan artikel ini ke teman satu kelompok praktikummu.

💬 Tulis Komentar📲 Bagikan via WhatsApp
Tag:
#PemrogramanWeb#D4MIK#LoginPHP#SessionPHP#Authentication
Label:
Login PHP SessionRegistrasi UserLogout PHPAuthenticationManajemen Informasi Kesehatan
📚 Artikel ini adalah bagian dari seri Pemrograman Web D4 MIK
Lihat Daftar Isi Artikel Pemrograman Web D4 MIK
Semua 16 artikel seri ini, dari dasar PHP sampai keamanan aplikasi.

relasi tabel kunjungan pasien

🏥🔗🗂️
Relasi Tabel MySQL Foreign Key JOIN Form Kunjungan

Hubungkan Pasien dan Poli: Relasi Tabel, Foreign Key, dan Form Pendaftaran Kunjungan Pasien (JOIN)

Artikel 12 dari 16 · Seri Pemrograman Web D4 MIK · CPMK 5, Sub-CPMK 8

⏱️
±12 menit
Estimasi baca
📊
Menengah–Lanjut
Level
📅
2026
Tahun

Bayangkan petugas pendaftaran mengetik nama pasien dan nama poli berulang-ulang di setiap kunjungan. Salah ketik satu huruf, "Poli Gigi" berubah jadi "Poli Gigy", dan laporan jumlah kunjungan langsung kacau. Solusinya adalah relasi tabel MySQL: data pasien dan poli disimpan satu kali, lalu dihubungkan lewat foreign key.

Artikel ini adalah bagian dari seri Pemrograman Web D4 MIK. Kamu akan membuat tabel kunjungan, menggabungkannya dengan JOIN, dan membangun form pendaftaran kunjungan pasien dengan PHP. Semua praktik memakai XAMPP, phpMyAdmin, dan Sublime Text dengan data fiktif. Siap menyambungkan dua modul yang sudah kamu buat?

Konsep Dasar Relasi Tabel MySQL: Pasien dan Poli Saling Terhubung

Bayangkan kartu berobat. Setiap pasien punya nomor kartu unik, dan setiap poli punya kode ruangan. Saat pasien datang, petugas tidak menulis ulang identitas lengkapnya. Petugas cukup mencatat "kartu nomor sekian, ke ruangan sekian". Catatan itulah tabel kunjungan. Pasien dan poli adalah data induk (master), sedangkan kunjungan adalah data transaksi.

📐 FORMULA KONSEP
Primary Key (tabel induk) ← Foreign Key (tabel anak)
pasien.id_pasien 1 ──< N kunjungan.id_pasien
poli.id_poli 1 ──< N kunjungan.id_poli
Foreign key adalah kolom di tabel anak yang nilainya wajib sama dengan primary key di tabel induk. Hubungan ini disebut one-to-many.
┌─────────────┐      ┌────────────────┐      ┌───────────┐
│   pasien    │ 1──N │   kunjungan    │ N──1 │   poli    │
│ id_pasien PK│◄─────│ id_pasien FK   │─────►│ id_poli PK│
│ no_rm       │      │ id_poli   FK   │      │ nama_poli │
│ nama_pasien │      │ tgl_kunjungan  │      └───────────┘
└─────────────┘      └────────────────┘
⚡ Insight Penting: Foreign key bekerja seperti satpam. MySQL akan menolak kunjungan untuk pasien atau poli yang tidak ada di tabel master. Dengan begitu, data "hantu" tidak bisa masuk.
TabelJenisKunciPeran
pasienMasterPK id_pasienIdentitas pasien
poliMasterPK id_poliDaftar pelayanan
kunjunganTransaksiFK id_pasien, id_poliMencatat siapa ke poli mana

Praktik 1: Membuat Relasi Tabel MySQL dengan Foreign Key

1
Nyalakan layanan.
Buka XAMPP Control Panel, klik Start pada Apache dan MySQL.
2
Buka phpMyAdmin.
Akses http://localhost/phpmyadmin, lalu pilih database db_klinik. Kalau belum ada, buat baru dengan nama itu.
3
Jalankan skrip SQL.
Klik tab SQL, tempel kode di bawah, lalu klik Go. Kalau tabel pasien dan poli dari artikel sebelumnya sudah ada, lewati dua CREATE pertama dan INSERT-nya. Pastikan tipe kolom primary key-nya sama dengan skrip ini.
📄 skrip_relasi.sql
CREATE TABLE IF NOT EXISTS pasien (
  id_pasien INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
  no_rm VARCHAR(10) NOT NULL UNIQUE,
  nama_pasien VARCHAR(100) NOT NULL
) ENGINE=InnoDB;

CREATE TABLE IF NOT EXISTS poli (
  id_poli INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
  nama_poli VARCHAR(50) NOT NULL
) ENGINE=InnoDB;

CREATE TABLE kunjungan (
  id_kunjungan INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
  id_pasien INT UNSIGNED NOT NULL,
  id_poli INT UNSIGNED NOT NULL,
  tgl_kunjungan DATE NOT NULL,
  keluhan VARCHAR(255),
  CONSTRAINT fk_kunjungan_pasien FOREIGN KEY (id_pasien)
    REFERENCES pasien(id_pasien) ON DELETE RESTRICT ON UPDATE CASCADE,
  CONSTRAINT fk_kunjungan_poli FOREIGN KEY (id_poli)
    REFERENCES poli(id_poli) ON DELETE RESTRICT ON UPDATE CASCADE
) ENGINE=InnoDB;

INSERT INTO pasien (no_rm, nama_pasien) VALUES
  ('RM001','Siti Aminah'), ('RM002','Budi Santoso');
INSERT INTO poli (nama_poli) VALUES
  ('Poli Umum'), ('Poli Gigi'), ('Poli KIA');
4
Cek hasilnya.
Buka tabel kunjungan, klik tab Structure → Relation view (atau menu Designer). Kamu akan melihat dua foreign key aktif.
🔥 Fakta Menarik: Menurut dokumentasi MySQL, foreign key hanya berfungsi pada mesin penyimpanan InnoDB. Karena itu skrip di atas memakai ENGINE=InnoDB. XAMPP versi terbaru sebenarnya memakai MariaDB, tetapi sintaks dan perilaku foreign key untuk praktik ini sama.
🔍 Analisis: Pilih Aturan ON DELETE yang Tepat
AturanPerilakuCocok untuk data medis?
RESTRICTMenolak hapus jika masih dipakai✅ Aman, riwayat kunjungan terjaga
CASCADEIkut menghapus data anak⚠️ Berbahaya, riwayat bisa hilang
SET NULLKolom FK diisi NULL❌ Kunjungan jadi tanpa pasien

JOIN dalam Relasi Tabel MySQL: Gabungkan Data Kunjungan, Pasien, dan Poli

Tabel kunjungan hanya berisi angka ID, jadi petugas tidak bisa membacanya. JOIN berfungsi seperti mencocokkan nomor kartu dengan buku besar identitas, sehingga angka berubah menjadi nama pasien dan nama poli. Jalankan dua kunjungan fiktif ini, lalu query JOIN-nya di tab SQL:

📄 query_join.sql
INSERT INTO kunjungan (id_pasien, id_poli, tgl_kunjungan, keluhan) VALUES
  (1, 2, '2026-10-12', 'Sakit gigi geraham'),
  (2, 1, '2026-10-12', 'Demam tiga hari');

SELECT k.id_kunjungan, p.no_rm, p.nama_pasien,
       pl.nama_poli, k.tgl_kunjungan, k.keluhan
FROM kunjungan k
INNER JOIN pasien p ON k.id_pasien = p.id_pasien
INNER JOIN poli pl ON k.id_poli = pl.id_poli
ORDER BY k.tgl_kunjungan DESC;

Hasil yang diharapkan: dua baris muncul dengan kolom nama_pasien (Siti Aminah, Budi Santoso) dan nama_poli (Poli Gigi, Poli Umum), bukan lagi angka 1 dan 2.

Jenis JOINHasilContoh kegunaan
INNER JOINHanya data yang cocok di kedua tabelDaftar kunjungan lengkap
LEFT JOINSemua data tabel kiri, yang tak cocok berisi NULLPoli yang belum punya kunjungan
💡 Tips: Beri alias pendek pada tabel (k, p, pl). Query jadi lebih ringkas, dan kamu terhindar dari error kolom ambigu.

Praktik 2: Form Pendaftaran Kunjungan dari Relasi Tabel MySQL ke PHP

1
Siapkan folder.
Buat folder C:\xampp\htdocs\klinik, lalu buka di Sublime Text.
2
Buat koneksi.php.
Kalau sudah punya file koneksi dari artikel sebelumnya, pakai saja.
📄 koneksi.php
<?php
mysqli_report(MYSQLI_REPORT_OFF); // error ditangani manual
$conn = mysqli_connect('localhost', 'root', '', 'db_klinik');
if (!$conn) {
    die('Koneksi gagal: ' . mysqli_connect_error());
}
3
Buat kunjungan.php.
File ini berisi tiga hal: proses simpan, form dengan dropdown dari tabel master, dan tabel hasil JOIN.
📄 kunjungan.php
<?php
include 'koneksi.php';
$pesan = '';
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $stmt = mysqli_prepare($conn, "INSERT INTO kunjungan
        (id_pasien, id_poli, tgl_kunjungan, keluhan) VALUES (?, ?, ?, ?)");
    mysqli_stmt_bind_param($stmt, 'iiss', $_POST['id_pasien'],
        $_POST['id_poli'], $_POST['tgl'], $_POST['keluhan']);
    $pesan = mysqli_stmt_execute($stmt)
        ? 'Kunjungan berhasil disimpan.'
        : 'Gagal: ' . mysqli_stmt_error($stmt);
}
$pasien = mysqli_query($conn, "SELECT id_pasien, no_rm, nama_pasien FROM pasien ORDER BY nama_pasien");
$poli   = mysqli_query($conn, "SELECT id_poli, nama_poli FROM poli ORDER BY nama_poli");
$data   = mysqli_query($conn, "SELECT k.id_kunjungan, p.no_rm, p.nama_pasien,
    pl.nama_poli, k.tgl_kunjungan, k.keluhan FROM kunjungan k
    JOIN pasien p ON k.id_pasien = p.id_pasien
    JOIN poli pl ON k.id_poli = pl.id_poli
    ORDER BY k.id_kunjungan DESC");
?>
<h2>Pendaftaran Kunjungan Pasien</h2>
<p><?= htmlspecialchars($pesan) ?></p>
<form method="post">
  <select name="id_pasien" required>
    <option value="">-- Pilih Pasien --</option>
    <?php while ($r = mysqli_fetch_assoc($pasien)) { ?>
      <option value="<?= $r['id_pasien'] ?>">
        <?= htmlspecialchars($r['no_rm'] . ' - ' . $r['nama_pasien']) ?></option>
    <?php } ?>
  </select>
  <select name="id_poli" required>
    <option value="">-- Pilih Poli --</option>
    <?php while ($r = mysqli_fetch_assoc($poli)) { ?>
      <option value="<?= $r['id_poli'] ?>">
        <?= htmlspecialchars($r['nama_poli']) ?></option>
    <?php } ?>
  </select>
  <input type="date" name="tgl" required>
  <input type="text" name="keluhan" placeholder="Keluhan">
  <button type="submit">Simpan</button>
</form>
<table border="1" cellpadding="6">
  <tr><th>No RM</th><th>Pasien</th><th>Poli</th><th>Tanggal</th><th>Keluhan</th></tr>
  <?php while ($d = mysqli_fetch_assoc($data)) { ?>
  <tr>
    <td><?= htmlspecialchars($d['no_rm']) ?></td>
    <td><?= htmlspecialchars($d['nama_pasien']) ?></td>
    <td><?= htmlspecialchars($d['nama_poli']) ?></td>
    <td><?= $d['tgl_kunjungan'] ?></td>
    <td><?= htmlspecialchars($d['keluhan']) ?></td>
  </tr>
  <?php } ?>
</table>
4
Uji di browser.
Buka http://localhost/klinik/kunjungan.php. Pilih pasien, poli, tanggal, dan isi keluhan, lalu klik Simpan. Hasil yang diharapkan: muncul pesan sukses, dan baris baru tampil paling atas di tabel dengan nama pasien serta nama poli.
⚡ Insight Penting: Dropdown mengambil data langsung dari tabel master, jadi petugas tidak bisa mengetik poli yang tidak ada. Tanda tanya (?) pada prepared statement juga melindungi form dari SQL injection, sesuai rekomendasi OWASP.

Error yang Sering Muncul pada Relasi Tabel MySQL & Cara Mengatasinya

Pesan ErrorPenyebabSolusi
#1452 Cannot add or update a child rowID pasien/poli tidak ada di tabel indukIsi data master dulu, pilih lewat dropdown
#1451 Cannot delete or update a parent rowData master masih dipakai kunjunganHapus kunjungan terkait dulu, atau jangan hapus data master
#1215 Cannot add foreign key constraintTipe kolom beda (misal INT vs INT UNSIGNED) atau engine bukan InnoDBSamakan tipe data dan gunakan InnoDB
#1052 Column ... is ambiguousNama kolom sama di dua tabelTulis dengan alias, misalnya p.nama_pasien
⚠️ Perhatian: Jangan menghapus tabel master sembarangan di phpMyAdmin hanya demi menghilangkan error #1451. Di sistem kesehatan nyata, riwayat kunjungan tidak boleh hilang.
✍️ Latihan Mandiri
  1. Filter kunjungan. Tambahkan form pencarian pada kunjungan.php untuk menampilkan kunjungan berdasarkan poli dan tanggal memakai WHERE.
  2. Rekap per poli. Buat query LEFT JOIN + GROUP BY yang menampilkan jumlah kunjungan setiap poli, termasuk poli yang jumlahnya 0.
  3. Uji integritas. Coba hapus pasien yang sudah punya kunjungan. Catat pesan errornya, lalu jelaskan dengan bahasamu sendiri mengapa MySQL menolaknya.
🎯 Kesimpulan

Kamu sudah menyambungkan data pasien dan master poli. Intinya, relasi tabel MySQL menyimpan setiap data sekali di tabel master, lalu menghubungkannya ke tabel kunjungan lewat foreign key. JOIN mengubah angka ID menjadi informasi yang bisa dibaca, dan form PHP dengan dropdown menjaga data tetap bersih.

Bagaimana hasil praktikmu? Ceritakan error yang kamu temui di kolom komentar, dan bagikan artikel ini ke teman sekelasmu!

💬 Tulis Komentar 📤 Bagikan via WhatsApp
📚 Referensi:
#PemrogramanWeb #D4MIK #ForeignKey #JOINMySQL #KunjunganPasien
🏷️ Label: Relasi Tabel MySQL Form Pendaftaran Kunjungan Pasien Foreign Key dan JOIN
📚
Pemrograman Web D4 MIK

Lihat seluruh 16 artikel dalam seri ini, dari dasar HTML sampai aplikasi web terintegrasi.

📖 Buka Daftar Isi Artikel

crud master poli php

🏥🩺

Kelola Master Poli: Modul CRUD Data Poliklinik

Poli Umum, Gigi, KIA, dan lainnya — bangun fondasi data pelayanan kesehatan dengan PHP dan MySQL, langkah demi langkah.

#MasterDataPoli #PHP & MySQL #CRUD #XAMPP
⏱️
±12 menit
Estimasi baca
📊
Menengah
Level
📅
2026
Tahun

Bayangkan kamu bertugas di loket pendaftaran. Pasien datang, lalu kamu bingung: poli gigi itu ditulis "Poli Gigi", "poli gigi", atau "Gigi & Mulut"? Satu rumah sakit, tiga ejaan, dan laporan kunjungan pun berantakan. Biang keroknya hampir selalu sama: master data poli yang tidak dikelola rapi.

Artikel ke-11 dari seri Pemrograman Web D4 MIK ini mengajakmu membangun modul CRUD untuk Poli Umum, Poli Gigi, Poli KIA, dan poli lainnya memakai PHP dan MySQL di XAMPP. Satu pertemuan praktikum cukup. Buka Sublime Text, kita mulai!

Memahami Master Data Poli: Fondasi Data Pelayanan Kesehatan

Analogi gampangnya begini: master data itu seperti daftar menu di restoran, sedangkan data transaksi seperti pesanan pelanggan. Menu cukup ditulis sekali dan jarang berubah, tapi setiap pesanan merujuk ke menu itu. Di fasilitas kesehatan, daftar poli adalah "menu"-nya, dan setiap kunjungan pasien adalah "pesanan"-nya.

📐 KONSEP UTAMA
Master data = data referensi yang jarang berubah dan dipakai oleh banyak transaksi.
[ POLI ] 1 ───── N [ KUNJUNGAN ] N ───── 1 [ PASIEN ]
 master            transaksi              master
Aspek Master Data Poli Data Kunjungan (Transaksi)
Contoh isiPoli Umum, Poli Gigi, Poli KIABudi berobat ke Poli Gigi, 12 Okt 2026
Frekuensi berubahJarang (saat ada poli baru)Setiap hari
Jumlah barisPuluhanRibuan sampai jutaan
Risiko jika salahLaporan semua poli ikut kacauSatu kunjungan keliru
⚡ Insight Penting
Karena master data poli dirujuk banyak tabel lain, kesalahan kecil di sini menjalar ke mana-mana. Itulah kenapa modul ini kita kerjakan sebelum modul kunjungan di Artikel 12.

Menyiapkan Database untuk Master Data Poli di phpMyAdmin

Sebelum menulis PHP, siapkan dulu "lemari arsip"-nya. Ikuti langkah berikut di komputer laboratorium (Windows):

1
Nyalakan layanan. Buka XAMPP Control Panel, klik Start pada Apache dan MySQL sampai keduanya berwarna hijau.
2
Buka phpMyAdmin. Ketik http://localhost/phpmyadmin di browser.
3
Pilih database. Pakai database db_klinik dari artikel sebelumnya. Kalau belum ada, buat dulu dengan collation utf8mb4_general_ci.
4
Jalankan SQL. Klik tab SQL, tempel kode di bawah, lalu klik Go.
📄 tabel_poli.sql
CREATE TABLE poli (
  id_poli      INT AUTO_INCREMENT PRIMARY KEY,
  kode_poli    VARCHAR(10) NOT NULL UNIQUE,
  nama_poli    VARCHAR(50) NOT NULL,
  lokasi_ruang VARCHAR(50),
  status       ENUM('aktif','nonaktif') NOT NULL DEFAULT 'aktif',
  created_at   TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

INSERT INTO poli (kode_poli, nama_poli, lokasi_ruang) VALUES
('POL-UM',  'Poli Umum', 'Gedung A, Ruang 1'),
('POL-GG',  'Poli Gigi', 'Gedung A, Ruang 2'),
('POL-KIA', 'Poli KIA',  'Gedung B, Ruang 1');

Hasil yang diharapkan: di sisi kiri phpMyAdmin muncul tabel poli, dan tab Browse menampilkan 3 baris data fiktif.

💡 Tips
Kolom kode_poli dibuat UNIQUE supaya database sendiri yang menolak kode ganda. Jangan hanya mengandalkan petugas yang teliti — manusia bisa lelah, constraint tidak.

Membangun Modul CRUD Master Data Poli dengan PHP dan MySQL

Buat folder C:\xampp\htdocs\klinik\, lalu simpan lima file berikut dengan Sublime Text. Kita kerjakan berurutan dari koneksi sampai hapus.

① Koneksi: koneksi.php

📄 koneksi.php
<?php
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
$koneksi = new mysqli("localhost", "root", "", "db_klinik");
$koneksi->set_charset("utf8mb4");

② Read: poli.php

📄 poli.php
<?php
require "koneksi.php";
$hasil = $koneksi->query("SELECT * FROM poli ORDER BY nama_poli ASC");
?>
<h2>Master Data Poli</h2>
<?php if (isset($_GET['pesan'])) echo "<p>" . htmlspecialchars($_GET['pesan']) . "</p>"; ?>
<a href="poli_tambah.php">+ Tambah Poli</a>
<table border="1" cellpadding="8" cellspacing="0">
  <tr><th>No</th><th>Kode</th><th>Nama Poli</th><th>Ruang</th><th>Status</th><th>Aksi</th></tr>
  <?php $no = 1; while ($row = $hasil->fetch_assoc()) { ?>
  <tr>
    <td><?= $no++ ?></td>
    <td><?= htmlspecialchars($row['kode_poli']) ?></td>
    <td><?= htmlspecialchars($row['nama_poli']) ?></td>
    <td><?= htmlspecialchars($row['lokasi_ruang']) ?></td>
    <td><?= $row['status'] ?></td>
    <td>
      <a href="poli_edit.php?id=<?= $row['id_poli'] ?>">Ubah</a> |
      <a href="poli_hapus.php?id=<?= $row['id_poli'] ?>" onclick="return confirm('Hapus poli ini?')">Hapus</a>
    </td>
  </tr>
  <?php } ?>
</table>

Hasil yang diharapkan: buka http://localhost/klinik/poli.php dan tabel berisi Poli Gigi, Poli KIA, dan Poli Umum (urut abjad) tampil lengkap dengan tautan Ubah dan Hapus.

⚠️ Perhatian
Data dari database selalu dibungkus htmlspecialchars() sebelum ditampilkan. Tanpa itu, nama poli yang berisi tag HTML bisa merusak halaman atau membuka celah XSS.

③ Create: poli_tambah.php

📄 poli_tambah.php
<?php
require "koneksi.php";
$error = "";
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $kode   = trim($_POST['kode_poli']);
    $nama   = trim($_POST['nama_poli']);
    $ruang  = trim($_POST['lokasi_ruang']);
    $status = $_POST['status'];
    if ($kode === "" || $nama === "") {
        $error = "Kode dan nama poli wajib diisi.";
    } else {
        try {
            $stmt = $koneksi->prepare("INSERT INTO poli (kode_poli, nama_poli, lokasi_ruang, status) VALUES (?, ?, ?, ?)");
            $stmt->bind_param("ssss", $kode, $nama, $ruang, $status);
            $stmt->execute();
            header("Location: poli.php?pesan=Poli+berhasil+ditambahkan");
            exit;
        } catch (mysqli_sql_exception $e) {
            $error = ($e->getCode() == 1062) ? "Kode poli sudah dipakai." : "Gagal menyimpan data.";
        }
    }
}
?>
<h2>Tambah Poli</h2>
<?php if ($error) echo "<p style='color:red'>" . htmlspecialchars($error) . "</p>"; ?>
<form method="post">
  Kode Poli <input type="text" name="kode_poli" maxlength="10"><br>
  Nama Poli <input type="text" name="nama_poli" maxlength="50"><br>
  Lokasi Ruang <input type="text" name="lokasi_ruang" maxlength="50"><br>
  Status <select name="status">
    <option value="aktif">Aktif</option>
    <option value="nonaktif">Nonaktif</option>
  </select><br>
  <button type="submit">Simpan</button> <a href="poli.php">Batal</a>
</form>

Hasil yang diharapkan: isi kode POL-ANK dan nama Poli Anak, klik Simpan, lalu kamu kembali ke daftar dengan pesan sukses. Coba kirim kode POL-UM lagi dan pesan "Kode poli sudah dipakai." akan muncul.

🔥 Fakta Menarik
Kategori Injection (termasuk SQL Injection) masuk daftar OWASP Top 10 edisi 2021 sebagai A03. Prepared statement dengan tanda ? seperti di kode ini adalah pertahanan utama yang direkomendasikan OWASP.

④ Update: poli_edit.php

📄 poli_edit.php
<?php
require "koneksi.php";
$id = (int) ($_GET['id'] ?? 0);
$stmt = $koneksi->prepare("SELECT * FROM poli WHERE id_poli = ?");
$stmt->bind_param("i", $id);
$stmt->execute();
$poli = $stmt->get_result()->fetch_assoc();
if (!$poli) { die("Data poli tidak ditemukan."); }

$error = "";
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $kode   = trim($_POST['kode_poli']);
    $nama   = trim($_POST['nama_poli']);
    $ruang  = trim($_POST['lokasi_ruang']);
    $status = $_POST['status'];
    try {
        $up = $koneksi->prepare("UPDATE poli SET kode_poli=?, nama_poli=?, lokasi_ruang=?, status=? WHERE id_poli=?");
        $up->bind_param("ssssi", $kode, $nama, $ruang, $status, $id);
        $up->execute();
        header("Location: poli.php?pesan=Poli+berhasil+diubah");
        exit;
    } catch (mysqli_sql_exception $e) {
        $error = ($e->getCode() == 1062) ? "Kode poli sudah dipakai." : "Gagal mengubah data.";
    }
}
?>
<h2>Ubah Poli</h2>
<?php if ($error) echo "<p style='color:red'>" . htmlspecialchars($error) . "</p>"; ?>
<form method="post">
  Kode Poli <input type="text" name="kode_poli" value="<?= htmlspecialchars($poli['kode_poli']) ?>"><br>
  Nama Poli <input type="text" name="nama_poli" value="<?= htmlspecialchars($poli['nama_poli']) ?>"><br>
  Lokasi Ruang <input type="text" name="lokasi_ruang" value="<?= htmlspecialchars($poli['lokasi_ruang']) ?>"><br>
  Status <select name="status">
    <option value="aktif" <?= $poli['status'] == 'aktif' ? 'selected' : '' ?>>Aktif</option>
    <option value="nonaktif" <?= $poli['status'] == 'nonaktif' ? 'selected' : '' ?>>Nonaktif</option>
  </select><br>
  <button type="submit">Perbarui</button> <a href="poli.php">Batal</a>
</form>

Hasil yang diharapkan: klik Ubah pada Poli Umum, form terisi data lama. Ganti ruang menjadi "Gedung A, Ruang 3", simpan, dan perubahan langsung terlihat di daftar.

⑤ Delete: poli_hapus.php

📄 poli_hapus.php
<?php
require "koneksi.php";
$id = (int) ($_GET['id'] ?? 0);
try {
    $stmt = $koneksi->prepare("DELETE FROM poli WHERE id_poli = ?");
    $stmt->bind_param("i", $id);
    $stmt->execute();
    $pesan = "Poli berhasil dihapus";
} catch (mysqli_sql_exception $e) {
    $pesan = ($e->getCode() == 1451)
        ? "Poli masih dipakai data lain, tidak bisa dihapus"
        : "Gagal menghapus data";
}
header("Location: poli.php?pesan=" . urlencode($pesan));
exit;
🔍 Analisis: Hapus Permanen atau Nonaktifkan?
🗑️ Hapus permanen (DELETE)
Cocok untuk data salah ketik yang belum dipakai. Baris hilang selamanya, dan MySQL menolak jika sudah dirujuk tabel lain lewat foreign key.
🔒 Nonaktifkan (status)
Cocok untuk poli yang sudah punya riwayat kunjungan. Poli tidak muncul di pendaftaran baru, tetapi riwayat lama tetap utuh.
Di dunia nyata, poli yang pernah melayani pasien sebaiknya dinonaktifkan, bukan dihapus. Kamu akan melihat alasannya di Artikel 12.

Error yang Sering Muncul & Cara Mengatasinya pada Master Data Poli

Error itu normal, bahkan pertanda kamu sedang belajar. Berikut lima yang paling sering muncul saat praktikum:

Pesan / Gejala Penyebab Solusi
Unknown database 'db_klinik'Nama database salah atau belum dibuatCek ejaan di phpMyAdmin, samakan dengan koneksi.php
Access denied for user 'root'Password MySQL tidak sesuaiDefault XAMPP kosong (""); sesuaikan jika laboratorium memakai password
Duplicate entry ... for key 'kode_poli'Kode poli sudah ada (kode error 1062)Gunakan kode lain; kode di atas sudah menangkapnya dengan try/catch
Kode PHP tampil mentah di browserFile dibuka via file:// atau disimpan berekstensi .htmlSimpan sebagai .php di htdocs, buka lewat http://localhost/klinik/
Cannot delete ... foreign key constraint failsPoli masih dirujuk tabel lain (kode error 1451)Nonaktifkan poli, atau hapus data yang merujuk lebih dulu
📝 Latihan Mandiri

Kerjakan tiga tugas berikut untuk mengasah Sub-CPMK 7 (mengembangkan modul master data pelayanan kesehatan):

  1. Tambah kolom jam_praktik (misalnya 08.00–14.00) lewat ALTER TABLE, lalu tampilkan dan ubah datanya di modul CRUD.
  2. Buat fitur pencarian nama poli di poli.php memakai LIKE dan prepared statement.
  3. Ganti tombol Hapus menjadi "Nonaktifkan" yang mengubah status menjadi nonaktif, lalu beri warna berbeda pada baris poli nonaktif.
⚡ Insight Penting
Pola yang kamu pelajari hari ini (tabel, koneksi, form, prepared statement) akan kamu pakai ulang untuk master dokter, master obat, hingga master diagnosis. Kuasai sekali, pakai berkali-kali.

🎯 Kesimpulan

Master data poli adalah fondasi yang dirujuk semua modul pelayanan. Hari ini kamu sudah membuat tabel poli dengan kode unik, lalu membangun CRUD lengkap: tampil, tambah, ubah, dan hapus, semuanya dengan prepared statement, htmlspecialchars(), dan penanganan error yang rapi.

Dengan master data poli yang bersih, Artikel 12 tentang relasi tabel dan kunjungan pasien akan jauh lebih mulus. Sudah berhasil menjalankannya? Ceritakan error yang kamu temui di kolom komentar, dan bagikan artikel ini ke teman sekelasmu!

💬 Tulis Komentar 🔗 Bagikan Artikel
🏷️ Tag
#PemrogramanWeb #D4MIK #MasterPoli #ModulCRUD #DataPoliklinik
📂 Label
Master Data Poli CRUD PHP MySQL Modul Poliklinik Praktikum MIK
📚
ARTIKEL UTAMA SERI
Pemrograman Web D4 MIK

Artikel ini adalah bagian ke-11 dari 16. Lihat seluruh rangkaian materi di Daftar Isi.

📖 Buka Daftar Isi Artikel
← ARTIKEL SEBELUMNYA
Artikel 10: Ubah dan Hapus dengan Aman: CRUD Bagian Update dan Delete Data Pasien (Konfirmasi Hapus, Prepared Statement)
Baca artikel →
ARTIKEL SELANJUTNYA →
Artikel 12: Hubungkan Pasien dan Poli: Relasi Tabel, Foreign Key, dan Form Pendaftaran Kunjungan Pasien (JOIN)
Baca artikel →

saifiahmada.com adalah blog belajar programming Indonesia, membahas lengkap materi bahasa pemrograman: code HTML, CSS, Bootstrap, Desain, PHP, MySQL, coding Java, Query, SQL, dan dunia linux