D
P
0
← Semua artikel Read in English

WordPress & PHP di Produksi

Listing Baru Tidak Muncul di Moderasi dan Pencarian? `_listing_status` Tidak Pernah Di-set Saat Dibuat

· · 6 menit baca
Listing Baru Tidak Muncul di Moderasi dan Pencarian? `_listing_status` Tidak Pernah Di-set Saat Dibuat

Ada bug yang teriak, dan ada bug yang diam. Yang ini diam, dan diamnya sempurna, karena setiap pihak yang terlibat melihat layar yang tampak wajar.

Saya sedang mengerjakan sebuah platform booking properti. Pemiliknya mendaftarkan listing sendiri lewat dashboard, lalu admin menyetujui dulu sebelum listing itu boleh tampil di publik. Alur moderasi standar. Pemilik submit, admin approve, listing masuk pencarian.

Yang terjadi di lapangan: listing yang dibuat pemilik tidak pernah sampai ke mana-mana. Tidak ke antrean moderasi, tidak ke pencarian. Selamanya.

Gejalanya: tiga layar yang semuanya kelihatan benar

Ini yang bikin kasusnya betah bersembunyi sekian lama.

Pemilik submit form, dapat notifikasi sukses, dan halaman "Listing Saya" langsung menampilkan properti barunya. Dari sisi dia, semuanya beres. Dia hanya berasumsi admin belum sempat mengecek.

Admin buka halaman moderasi, dan halaman itu kosong. Bukan error, bukan layar putih, hanya empty state yang sopan bertuliskan tidak ada yang menunggu persetujuan. Dari sisi admin, itu kabar baik. Antrean bersih.

Pengunjung buka halaman pencarian, dan listing itu memang tidak ada. Wajar juga, kan, kalau belum disetujui.

Tiga orang, tiga kesimpulan yang masuk akal, dan tidak satu pun dari mereka melihat sesuatu yang layak dilaporkan sebagai bug. Laporan baru muncul saat QA menjelang rilis, waktu ada yang iseng membandingkan dua angka: jumlah listing yang masuk terus bertambah, antrean moderasi masih nol.

Menelusuri: post-nya ada, cuma tidak ikut hitungan

Langkah pertama yang paling murah adalah memastikan datanya benar-benar tersimpan. Saya buka wp-admin dan listing itu ada di sana, lengkap dengan judul, harga, deskripsi, foto. Jadi endpoint REST-nya bekerja, wp_insert_post() sukses, post-nya nyata.

Berarti masalahnya bukan penyimpanan, tapi pengambilan. Saya buka query yang dipakai halaman moderasi. Bentuknya kira-kira begini:

$pending = get_posts( array(
    'post_type'      => 'listing',
    'posts_per_page' => 20,
    'meta_query'     => array(
        array(
            'key'   => '_listing_status',
            'value' => 'pending',
        ),
    ),
) );

Query pencarian publik memakai pola yang sama, hanya dengan nilai approved. Dua-duanya menyaring lewat meta yang sama.

Jadi saya periksa meta listing barunya. Kosong. Bukan berisi nilai yang salah, bukan berisi string kosong. Baris _listing_status untuk post itu memang tidak pernah ada.

Akar masalahnya: satu baris yang tidak pernah ditulis

Saya buka handler create_item di controller REST-nya. Dia menyusun $postarr, memanggil wp_insert_post(), menyimpan harga, kapasitas, alamat, galeri, lalu mengembalikan respons sukses. Semua meta bisnis tertulis rapi.

Satu-satunya yang tidak pernah ditulis adalah status moderasinya.

// create_item menulis semuanya kecuali yang paling menentukan
update_post_meta( $id, '_listing_price', $price );
update_post_meta( $id, '_listing_capacity', $capacity );
update_post_meta( $id, '_listing_gallery', $gallery_ids );
// tidak ada baris untuk _listing_status

Akibatnya listing baru mendarat di limbo. Dia bukan pending, bukan approved, bukan rejected. Dia tidak punya status sama sekali.

Dan di sinilah semantik meta_query WordPress menutup pintunya rapat-rapat. meta_query diterjemahkan jadi JOIN ke tabel postmeta. Kalau baris meta-nya tidak ada, tidak ada yang bisa dibandingkan, jadi post-nya gugur dari hasil sebelum perbandingan nilai apa pun sempat berjalan. Meta yang absen tidak sama dengan nilai apa pun, dan yang sering bikin orang tertipu, dia juga tidak lolos perbandingan negatif seperti != atau NOT IN. Untuk menangkapnya kamu harus minta secara eksplisit lewat NOT EXISTS.

Artinya listing tanpa status bukan cuma tidak muncul di satu tempat. Dia tidak muncul di mana pun yang menyaring lewat meta itu. Halaman moderasi menyaring pending, jadi lewat. Pencarian menyaring approved, jadi lewat juga. Tidak ada satu pun view yang kebetulan menangkapnya, karena semua view memang dibangun di atas kontrak yang sama.

Kontrak itu punya dua sisi: pembaca yang menyaring, dan penulis yang mengisi. Sisi pembacanya lengkap. Sisi penulisnya bolong, dan tidak ada yang protes karena data yang absen tidak melempar error.

Perbaikan bagian satu: tulis status default saat pembuatan

Perbaikan intinya sesingkat itu. Begitu post berhasil dibuat, langsung beri dia status awal:

$id = wp_insert_post( $postarr, true );
 
if ( is_wp_error( $id ) ) {
    return $id;
}
 
update_post_meta( $id, '_listing_status', 'pending' );

Yang penting di sini bukan panjang kodenya, tapi keputusannya: status default harus ditulis, bukan disiratkan. Selama ini kodenya seolah menganggap "tidak punya status" itu sama dengan "menunggu persetujuan". Manusia memang membacanya begitu, tapi query tidak. Buat query, tidak punya status berarti tidak memenuhi syarat apa pun.

Perbaikan bagian dua: backfill listing yang terlanjur limbo

Menambal create_item hanya menyelamatkan listing yang akan datang. Listing yang sudah terlanjur masuk selama berminggu-minggu tetap tidak terlihat, dan itu properti asli milik orang asli yang mengira mereka sedang antre.

Jadi saya pasang backfill sekali jalan di admin_init, dikunci lewat option supaya tidak berulang:

add_action( 'admin_init', 'maybe_backfill_listing_status' );
 
function maybe_backfill_listing_status() {
    if ( get_option( 'listing_status_backfilled' ) ) {
        return;
    }
 
    $ids = get_posts( array(
        'post_type'      => 'listing',
        'post_status'    => 'publish',
        'posts_per_page' => -1,
        'fields'         => 'ids',
        'meta_query'     => array(
            array(
                'key'     => '_listing_status',
                'compare' => 'NOT EXISTS',
            ),
        ),
    ) );
 
    foreach ( $ids as $id ) {
        update_post_meta( $id, '_listing_status', 'pending' );
    }
 
    update_option( 'listing_status_backfilled', 1 );
}

Tiga hal yang saya jaga di potongan ini. NOT EXISTS dipakai supaya yang tersentuh benar-benar cuma yang statusnya absen, bukan yang sudah punya nilai. Semuanya di-set ke pending, bukan approved, karena backfill tidak berhak mengambil keputusan moderasi atas nama admin. Dan option gate-nya bukan kemewahan: tanpa itu, query berat ini ikut jalan di setiap request admin selamanya.

Verifikasi

Saya tidak mau percaya sampai melihat siklus penuhnya berjalan. Jadi saya buka wp-admin sekali untuk memicu backfill, lalu buka halaman moderasi. Listing lama yang tadinya hilang sekarang berdiri di sana dengan label Pending. Saya klik Approve, statusnya berpindah ke approved, lalu saya buka halaman pencarian publik. Listing-nya muncul.

Baru setelah itu saya anggap selesai. Backfill yang cuma "kelihatan jalan" tapi tidak pernah diverifikasi sampai ke ujung alurnya sama saja dengan menukar satu limbo dengan limbo lain.

Diagnostik cepat kalau kamu curiga punya masalah serupa

Kapan pun kamu punya query yang menyaring lewat meta, ada baiknya sesekali menghitung berapa baris yang tidak punya meta itu sama sekali. Lewat PHP, pakai NOT EXISTS seperti di atas. Kalau lebih suka langsung di database:

SELECT p.ID, p.post_title
FROM wp_posts p
LEFT JOIN wp_postmeta m
  ON m.post_id = p.ID AND m.meta_key = '_listing_status'
WHERE p.post_type = 'listing'
  AND p.post_status = 'publish'
  AND m.meta_id IS NULL;

Kalau angkanya bukan nol, kamu punya sekumpulan baris yang tidak akan pernah muncul di view mana pun. Dan karena mereka tidak muncul, mereka juga tidak akan pernah melapor.

Yang saya bawa pulang