Moderasi Komunitas & Backed Enum
Siklus hidup konten buatan pengguna (User-Generated Content) yang dikontrol ketat melalui state machine sederhana berbasis PHP 8.3 Backed Enum.
Membaca sekitar 2 menit
Siklus hidup konten buatan pengguna (User-Generated Content) yang dikontrol ketat melalui state machine sederhana berbasis PHP 8.3 Backed Enum.
Setiap kiriman warga masuk sebagai status pending dengan slug unik acak, dan baru tampil ke publik setelah disetujui admin melalui scope Eloquent.
Menaruh logika label dan warna status langsung di dalam Enum DestinationStatus membuat kode Blade dan Controller bersih dari percabangan berulang.
Masalahnya dulu
Yogyakarta punya banyak destinasi tersembunyi (hidden gems) yang lebih dulu diketahui warga lokal daripada admin website. Membuka formulir pengajuan publik sangat membantu memperkaya database, tetapi menerima kiriman tanpa saringan berisiko memunculkan spam, duplikasi slug URL, atau informasi yang belum terverifikasi di halaman utama.
Memisahkan pintu masuk warga dan kurasi admin dengan Enum
Semua kiriman dari halaman publik masuk melalui SubmissionService dengan status awal pending dan akhiran 5 karakter acak pada slug untuk mencegah bentrok URL. Di sisi model, kolom status di-cast langsung ke PHP 8.3 Backed Enum (DestinationStatus) yang merangkum nilai database sekaligus metode presentasi label() dan color(). Seluruh query di halaman publik wajib melewati local scope Destination::approved() sehingga data yang belum disetujui mustahil bocor ke pengunjung.
Biarkan pintu kontribusi komunitas terbuka lebar, tetapi pastikan gerbang publikasi dijaga oleh satu sumber kebenaran status yang ketat.
Implementasi di ngodingan ini
Definisi Backed Enum DestinationStatus beserta penggunaannya pada model Destination dan proses kurasi di DestinationService:
enum DestinationStatus: string {
case Pending = 'pending';
case Approved = 'approved';
case Rejected = 'rejected';
public function label(): string {
return match($this) {
self::Pending => 'Menunggu Review',
self::Approved => 'Disetujui',
self::Rejected => 'Ditolak',
};
}
}
// Di DestinationService.php:
public function approve(Destination $destination): void {
$destination->update([
'status' => DestinationStatus::Approved,
'rejection_note' => null,
'published_at' => now(),
]);
}Jebakan dan detail yang harus dijaga
- Jika dua warga mengirimkan destinasi dengan nama yang sama persis sebelum di-review, Str::slug($title) biasa akan melempar Integrity constraint violation karena kolom slug bersifat unique. Penambahan sufiks acak di SubmissionService menghindari error tersebut bagi pengirim publik.
- Saat destinasi yang semula ditolak (memiliki rejection_note) kemudian diperbaiki dan disetujui oleh admin, metode approve() secara eksplisit mengosongkan kembali rejection_note menjadi null dan mengisi timestamp published_at.
- Di halaman manajemen admin, pencarian destinasi mendukung filter spesifik per kolom (title, district, atau submitter_name) dengan validasi whitelist array in_array($field, $allowedFields, true) agar parameter query aman dari manipulasi nama kolom.