Home / Artikel / Web Development
Web Development

Soft Delete di PHP dan MySQL: Menghapus Data tanpa Kehilangan Jejak

Menghapus baris dari database secara permanen terlihat sederhana, tetapi bisa menyulitkan saat pengguna ingin memulihkan data atau tim perlu menelusuri riwayat perubahan. Soft delete menawarkan jalan tengah—asal diranca…

Soft Delete di PHP dan MySQL: Menghapus Data tanpa Kehilangan Jejak

Tombol “Hapus” sering dianggap sebagai operasi sederhana: jalankan DELETE, lalu data hilang. Masalahnya muncul ketika pengguna tidak sengaja menghapus pesanan, admin perlu memeriksa riwayat akun, atau sebuah data harus dipulihkan tanpa mengambil backup seluruh database.

Di sinilah soft delete berguna. Alih-alih menghapus baris secara permanen, aplikasi menandai data sebagai tidak aktif—biasanya dengan kolom deleted_at atau is_deleted. Data masih tersimpan, tetapi tidak lagi ditampilkan dalam operasi normal.

Pola ini bukan pengganti backup dan bukan alasan untuk menyimpan semua data selamanya. Namun, untuk banyak aplikasi PHP dan MySQL, soft delete dapat membuat penghapusan lebih aman dan proses audit lebih mudah.

Apa itu soft delete?

Pada hard delete, aplikasi benar-benar menghapus baris dari tabel:

DELETE FROM users WHERE id = 42;

Setelah perintah itu berhasil, data tidak tersedia lagi melalui query biasa. Pemulihan hanya mungkin jika ada backup, binlog, atau mekanisme pencatatan lain.

Dengan soft delete, aplikasi cukup mengubah penanda:

UPDATE users
SET deleted_at = NOW()
WHERE id = 42 AND deleted_at IS NULL;

Baris tetap ada, tetapi query aplikasi perlu hanya mengambil data dengan deleted_at IS NULL. Nilai NULL berarti data masih aktif, sedangkan tanggal pada kolom tersebut menunjukkan kapan data “dihapus”.

Mengapa memakai deleted_at, bukan hanya is_deleted?

Kolom is_deleted memang lebih mudah dibaca: nilainya bisa 0 atau 1. Namun, kolom tanggal biasanya lebih informatif karena menyimpan waktu kejadian.

  • Tim dapat mengetahui kapan data dinonaktifkan.
  • Proses pemulihan dapat mencatat umur data yang sudah dihapus.
  • Admin dapat membersihkan data lama berdasarkan tanggal.
  • Audit menjadi lebih mudah ketika digabungkan dengan identitas pengguna yang melakukan tindakan.

Untuk kebutuhan yang lebih lengkap, tambahkan kolom seperti deleted_by atau buat tabel audit tersendiri. Jangan mencampur semua kebutuhan ke dalam satu kolom jika aplikasi memerlukan riwayat perubahan yang rinci.

Struktur tabel yang sederhana

Contoh tabel pengguna dapat dibuat seperti berikut:

CREATE TABLE users (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
email VARCHAR(255) NOT NULL,
name VARCHAR(150) NOT NULL,
deleted_at DATETIME NULL,
created_at DATETIME NOT NULL,
updated_at DATETIME NOT NULL,
INDEX idx_users_deleted_at (deleted_at)
);

Indeks pada deleted_at dapat membantu ketika tabel sudah besar dan hampir semua query menyaring data aktif. Namun, kebutuhan indeks tetap perlu diuji dengan data dan pola query yang nyata. Jangan menambahkan indeks secara membabi buta karena setiap indeks juga menambah pekerjaan saat INSERT dan UPDATE.

Aturan terpenting: query aktif harus konsisten

Kesalahan paling umum dalam soft delete bukan pada proses penandaan, melainkan pada query yang lupa menyaring data terhapus.

Query seperti ini berisiko menampilkan akun yang seharusnya sudah tidak aktif:

SELECT id, name, email
FROM users
ORDER BY id DESC;

Versi yang lebih aman:

SELECT id, name, email
FROM users
WHERE deleted_at IS NULL
ORDER BY id DESC;

Masalahnya, pada aplikasi besar, filter tersebut harus ditambahkan di banyak tempat: daftar pengguna, pencarian, laporan, relasi pesanan, endpoint API, dan halaman admin. Satu query yang terlewat bisa membuat data “terhapus” muncul lagi.

Karena itu, buat kebiasaan atau abstraksi yang jelas. Misalnya, pisahkan fungsi untuk mengambil data aktif dan fungsi khusus untuk mengambil data termasuk yang sudah dihapus.

function getActiveUsers(PDO $db): array
{
$sql = 'SELECT id, name, email
FROM users
WHERE deleted_at IS NULL
ORDER BY id DESC';

return $db->query($sql)->fetchAll(PDO::FETCH_ASSOC);
}

Nama fungsi seperti getActiveUsers membantu pembaca kode memahami aturan yang sedang dipakai. Untuk framework, gunakan fitur global scope atau repository jika tersedia, tetapi tetap beri jalan eksplisit untuk kebutuhan admin dan audit.

Jangan lupa aturan unik pada email atau username

Soft delete sering menimbulkan pertanyaan: jika akun dengan email user@example.com sudah ditandai terhapus, apakah pengguna boleh mendaftar lagi dengan email yang sama?

Jika kolom email memiliki UNIQUE biasa, pendaftaran baru akan ditolak karena baris lama masih ada. Ini bisa benar atau salah, tergantung aturan bisnis.

Ada beberapa pilihan:

  • Tidak mengizinkan penggunaan ulang. Cocok jika identitas akun harus tetap unik selamanya.
  • Mengizinkan penggunaan ulang setelah penghapusan. Aplikasi perlu memeriksa data aktif dan merancang indeks unik yang sesuai dengan kemampuan database.
  • Mengarsipkan identitas lama. Email pada baris lama dapat diubah menjadi nilai internal, tetapi tindakan ini harus dipahami karena memengaruhi audit dan pemulihan.

Jangan mengambil keputusan ini hanya dari sisi teknis. Tanyakan apa yang terjadi pada pesanan, histori pembayaran, dan proses pemulihan akun.

Proses restore harus diperlakukan sebagai operasi penting

Pemulihan data bukan sekadar mengubah deleted_at kembali menjadi NULL. Aplikasi perlu memeriksa apakah data yang dipulihkan masih konsisten.

UPDATE users
SET deleted_at = NULL,
updated_at = NOW()
WHERE id = 42
AND deleted_at IS NOT NULL;

Sebelum menjalankan proses tersebut, periksa konflik seperti email yang sudah dipakai akun lain, organisasi yang sudah dihapus, atau hak akses yang berubah. Pemulihan sebaiknya dibatasi untuk peran tertentu dan dicatat dalam log.

Soft delete bukan pengganti retensi dan backup

Menyimpan data dengan penanda terhapus tidak berarti data aman untuk selamanya. Baris yang tidak pernah dibersihkan akan terus menambah ukuran tabel, indeks, dan waktu backup.

Tetapkan kebijakan retensi. Contohnya, data yang sudah dihapus selama 90 hari dapat dipindahkan ke tabel arsip atau dihapus permanen melalui proses terjadwal. Sebelum itu, pastikan kebutuhan pemulihan, audit, dan aturan internal sudah terpenuhi.

Backup tetap diperlukan karena soft delete tidak melindungi dari kerusakan database, kesalahan massal, akses tidak sah, atau bug yang mengubah banyak baris sekaligus.

Yang bisa dilakukan sekarang

  1. Tambahkan deleted_at DATETIME NULL pada tabel yang memang membutuhkan pemulihan.
  2. Ubah semua query utama agar hanya mengambil data dengan deleted_at IS NULL.
  3. Definisikan dengan jelas siapa yang boleh menghapus dan memulihkan data.
  4. Uji konflik pada kolom unik seperti email, slug, dan nomor dokumen.
  5. Buat log untuk tindakan hapus dan restore.
  6. Tentukan kapan data lama diarsipkan atau dihapus permanen.

Soft delete paling berguna ketika diperlakukan sebagai bagian dari desain data, bukan sekadar menambah satu kolom. Dengan aturan query yang konsisten dan kebijakan retensi yang jelas, aplikasi dapat memberi ruang untuk memperbaiki kesalahan tanpa membuat database menjadi tempat penyimpanan tanpa batas.

– Rio Yotto @rioyotto