meta_query dengan nilai meta sebagai array serial

37

Saya sedang mengerjakan proyek tempat saya membuat jenis posting khusus dan data khusus yang dimasukkan melalui kotak meta yang terkait dengan jenis posting kustom saya. Untuk alasan apa pun saya memutuskan untuk memberi kode pada kotak meta sedemikian rupa sehingga input di setiap kotak metabox adalah bagian dari array. Misalnya, saya menyimpan garis bujur dan lintang:

<p> 
    <label for="latitude">Latitude:</label><br /> 
    <input type="text" id="latitude" name="coordinates[latitude]" class="full-width" value="" /> 
</p> 
<p>     
    <label for="longitude">Longitude:</label><br /> 
    <input type="text" id="longitude" name="coordinates[longitude]" class="full-width" value="" /> 
</p>

Untuk alasan apa pun, saya menyukai gagasan memiliki entri postmeta tunggal untuk setiap metabox. Di save_posthook, saya menyimpan data seperti ini:

update_post_meta($post_id, '_coordinates', $_POST['coordinates']);

Saya melakukan ini karena saya memiliki tiga metabox dan saya suka hanya memiliki 3 nilai postmeta untuk setiap posting; Namun, saya sekarang menyadari masalah potensial dengan ini. Saya mungkin ingin menggunakan WP_Query hanya untuk menarik posting tertentu berdasarkan nilai-nilai meta ini. Misalnya, saya mungkin ingin mendapatkan semua posting yang memiliki nilai garis lintang di atas 50. Jika saya memiliki data ini di database secara individual, mungkin menggunakan kunci latitude, saya akan melakukan sesuatu seperti:

$args = array(
    'post_type' => 'my-post-type',
    'meta_query' => array(
        array(
            'key' => 'latitude',
            'value' => '50',
            'compare' => '>'
        )
    )
 );
$query = new WP_Query( $args );

Karena saya memiliki garis lintang sebagai bagian dari _coordinatespostmeta, ini tidak akan berhasil.

Jadi, pertanyaan saya adalah, apakah ada cara untuk memanfaatkan meta_queryuntuk meminta array serial seperti yang saya miliki dalam skenario ini?

tollmanz
sumber

Jawaban:

37

Tidak, itu tidak mungkin, dan bahkan bisa berbahaya.

Saya sangat menyarankan Anda untuk membatalkan pengunaan data Anda dan memodifikasi rutin penyimpanan Anda. Sesuatu yang mirip dengan ini harus mengubah data Anda ke format baru:

$args = array(
    'post_type' => 'my-post-type',
    'meta_key' => '_coordinates',
    'posts_per_page' => -1
 );
$query = new WP_Query( $args );
if($query->have_posts()){
    while($query->have_posts()){
        $query->the_post();
        $c = get_post_meta($post->id,'_coordinates',true);
        add_post_meta($post->ID,'_longitude',$c['longitude']);
        add_post_meta($post->ID,'_latitude',$c['latitude']);
        delete_post_meta($post->ID,'_coordinates',$c);
    }
}

Maka Anda akan dapat melakukan query seperti yang Anda inginkan dengan kunci individual

Jika Anda perlu menyimpan beberapa garis bujur, dan beberapa garis lintang, Anda dapat menyimpan beberapa meta pos dengan nama yang sama. Cukup gunakan parameter ketiga get_post_meta, dan itu akan mengembalikan semuanya sebagai array

Mengapa Anda Tidak Dapat Meminta Data Serialized?

MySQL melihatnya hanya sebagai string, dan tidak dapat memecahnya menjadi data terstruktur. Memecahnya menjadi data terstruktur persis seperti yang dilakukan oleh kode di atas

Anda mungkin dapat meminta potongan parsial tanggal, tetapi ini akan menjadi sangat tidak dapat diandalkan, mahal, lambat, dan sangat rapuh, dengan banyak kasing tepi. Data berseri tidak dimaksudkan untuk kueri SQL, dan tidak diformat secara teratur dan konstan.

Selain dari biaya pencarian string parsial, permintaan meta posting lambat, dan data serial dapat berubah tergantung pada hal-hal seperti panjang konten, membuat pencarian menjadi sangat mahal, jika bukan tidak mungkin tergantung pada nilai yang Anda cari

Catatan tentang Menyimpan Catatan / Entitas / Objek sebagai Objek Serial di Meta

Anda mungkin ingin menyimpan catatan transaksi di pos meta, atau beberapa jenis struktur data lain di meta pengguna, kemudian mengalami masalah di atas.

Solusinya di sini bukanlah memecahnya menjadi meta pos individu, tetapi untuk merealisasikannya semestinya tidak pernah menjadi meta untuk memulai, tetapi jenis pos kiriman khusus. Misalnya, log atau catatan dapat berupa jenis pos khusus, dengan pos asli sebagai induk, atau digabungkan melalui istilah taksonomi

Keamanan dan Objek Serial

Menyimpan objek PHP serial melalui serializefungsi bisa berbahaya , yang disayangkan karena meneruskan objek ke WordPress akan berarti serialisasi. Ini karena ketika objek diderialisasi, sebuah objek dibuat, dan semua metode bangun dan konstruktor dijalankan. Ini mungkin tidak tampak seperti masalah besar sampai seorang pengguna berhasil menyelinap input yang dibuat dengan hati-hati, yang mengarah ke eksekusi kode jauh ketika data dibaca dari database dan di-serial oleh WordPress.

Ini dapat dihindari dengan menggunakan JSON sebagai gantinya, yang juga membuat kueri lebih mudah, tetapi jauh lebih mudah / lebih cepat untuk hanya menyimpan data dengan benar dan menghindari terstrukturnya data berseri.

Tom J Nowell
sumber
5
Untuk orang yang lewat, jangan berhenti membaca: jawaban yang lebih berguna (dan baru-baru ini) ditemukan di bawah ini
Erenor Paz
Bagaimana jika saya memiliki array ID untuk menyimpan - dan mereka masing-masing tidak mewakili kunci yang berbeda saya bisa menyimpannya di bawah seperti 'garis lintang' dll., Itu hanya satu kunci untuk semua (seperti ketika menyimpan hubungan dll). Lalu apa yang harus dilakukan? @ solusi rabni?
trainoasis
1
Anda dapat menyimpan kunci lebih dari sekali, pasangan nilai kunci tidak unik. Sedangkan untuk hubungan, itulah gunanya taksonomi, jika Anda menggunakan meta untuk memetakan banyak hal pada sesuatu, masukkan saja dalam istilah taksonomi
Tom J Nowell
24

Saya juga mengalami situasi ini. Inilah yang saya lakukan:

$args = array(
    'post_type' => 'my-post-type',
    'meta_query' => array(
        array(
            'key' => 'latitude',
            'value' => sprintf(':"%s";', $value),
            'compare' => 'LIKE'
        )
    )
);

Semoga bantuan ini

rabni
sumber
1
Saya sangat menyukai solusi ini. Sayangnya, ini tidak berlaku ketika $valueID juga. Dalam hal ini, saya sarankan untuk membuat fungsi untuk menambahkan karakter ke setiap elemen array sebelum menyimpan data dan fungsi lain untuk menghapus karakter sebelum menggunakan data. Ini wat, i:2indeks serial tidak akan bingung dengan i:D2data "nyata". Parameter meta query seharusnya menjadi 'value' => sprintf(':"D%s";', $value),dan Anda akan menjaga fungsionalitas yang benar dari jawaban yang luar biasa ini!
Erenor Paz
Solusi ini bekerja untuk saya
Vishal
Ini juga bekerja dengan baik untuk saya. Memang ada kepanikan mini ketika saya melihat solusi yang diterima
Shane Jones
@Erenor Paz, saya baru saja memposting solusi yang berfungsi baik dengan ID dan Strings: wordpress.stackexchange.com/a/299325/25264
Pablo SG Pacheco
menggunakan LIKEadalah cara yang bagus dan cepat untuk menjatuhkan server Anda (belum lagi positif palsu) Anda sebaiknya memiliki caching yang sangat baik.
Mark Kaplun
10

Anda benar-benar akan kehilangan kemampuan untuk meng-query data Anda dengan cara yang efisien ketika membuat serial entri ke dalam database WP.

Penghematan dan perolehan kinerja secara keseluruhan yang Anda pikir Anda capai dengan serialisasi tidak akan terlihat sampai batas tertentu. Anda mungkin mendapatkan ukuran basis data yang sedikit lebih kecil tetapi biaya transaksi SQL akan menjadi berat jika Anda pernah meminta bidang-bidang itu dan mencoba membandingkannya dengan cara yang bermanfaat dan bermakna.

Alih-alih, simpan serialisasi untuk data yang tidak ingin Anda tanyakan di alam itu, tetapi sebaliknya hanya akan mengakses secara pasif oleh panggilan API WP langsung get_post_meta()- dari fungsi itu Anda dapat membongkar entri serial untuk mengakses properti arraynya juga.

Bahkan diberi nilai true seperti pada;

$meta = get_post_meta( $post->ID, 'key', true );

Akan mengembalikan data sebagai array, dapat diakses bagi Anda untuk beralih seperti biasa.

Anda dapat fokus pada optimasi database / situs lain seperti caching, CSS dan JS minification dan menggunakan layanan seperti CDN jika Anda memerlukannya. Untuk beberapa nama .... WordPress Codex adalah titik awal yang baik untuk mengungkap lebih banyak tentang topik itu: DI SINI

Adam
sumber
3

Saya baru saja berurusan dengan bidang serial dan bisa menanyakannya. Tidak menggunakan meta_query tetapi menggunakan kueri SQL.

global $wpdb; 

$search = serialize('latitude').serialize(50);

$query = $wpdb->prepare("SELECT `post_id`
FROM `wp_postmeta`
WHERE `post_id` IN (SELECT `ID` FROM `wp_posts` WHERE `post_type` = 'my-post-type')
AND `meta_key` = '_coordinates'
AND `meta_value` LIKE '%s'",'%'.$search.'%');

$ids = $wpdb->get_col($query);

$args = array(
    'post__in' => $ids
    'post_type' => 'team' //add the type because the default will be 'post'
);

$posts = get_posts($args);

Permintaan pertama mencari posting dengan post_type yang cocok sehingga jumlah catatan wp_postmeta akan lebih sedikit untuk difilter. Lalu saya telah menambahkan pernyataan di mana untuk mengurangi baris lebih lanjut dengan memfiltermeta_key

ID berakhir dengan baik dalam array yang diperlukan untuk get_posts.

PS. MySQL v5.6 atau lebih tinggi diperlukan untuk kinerja subquery yang bagus

Tomas
sumber
1

Contoh ini sangat membantu saya. Ini khusus untuk plugin S2Members (yang membuat serial pengguna metadata). Tapi itu memungkinkan Anda untuk meminta sebagian dari array serial dalam meta_key.

Ia bekerja dengan menggunakan fungsi MySQL REGEXP.

Ini sumbernya

Berikut adalah kode yang menanyakan semua pengguna yang tinggal di AS. Saya dengan mudah memodifikasinya untuk meminta salah satu bidang pendaftaran kustom saya dan membuatnya berfungsi dalam waktu singkat.

  <?php
global $wpdb;
$users = $wpdb->get_results ("SELECT `user_id` as `ID` FROM `" . $wpdb->usermeta . 
          "` WHERE `meta_key` = '" . $wpdb->prefix . "s2member_custom_fields' AND 
           `meta_value` REGEXP '.*\"country_code\";s:[0-9]+:\"US\".*'");
if (is_array ($users) && count ($users) > 0)
    {
        foreach ($users as $user)
            {
                $user = /* Get full User object now. */ new WP_User ($user->ID);
                print_r($user); /* Get a full list of properties when/if debugging. */
            }
    }
?>
SM Smith
sumber
1

Saya pikir ada 2 solusi yang dapat mencoba memecahkan masalah hasil yang disimpan sebagai String dan Integer. Namun, penting untuk mengatakan, seperti yang ditunjukkan oleh orang lain, bahwa tidak mungkin untuk menjamin integritas hasil yang disimpan sebagai Integer, karena nilai-nilai ini disimpan sebagai array serial, indeks dan nilai disimpan persis dengan pola yang sama. Contoh:

array(37,87);

disimpan sebagai array serial, seperti ini

a:2:{i:0;i:37;i:1;i:87;}

Catat i:0sebagai posisi pertama array dan i:37sebagai nilai pertama. Polanya sama. Tapi mari kita pergi ke solusinya


1) Solusi REGEXP

Solusi ini berfungsi untuk saya terlepas dari nilai meta yang disimpan sebagai string atau angka / id. Namun ia menggunakan REGEXP, yang tidak secepat menggunakanLIKE

$args = array(
    'post_type' => 'my-post-type',
    'meta_query' => array(
        array(
            'key' => 'latitude',
            'value' => '\;i\:' . $value . '\;|\"' . $value . '\";',
            'compare' => 'REGEXP'
        )
    )
);

2) SEPERTI Solusi

Saya tidak yakin tentang perbedaan kinerja tetapi ini adalah solusi yang menggunakan LIKEdan juga berfungsi untuk angka dan string

 $args = array(
        'post_type' => 'my-post-type',
        'meta_query' => array(
            'relation' => 'OR',
            array(
                'key' => 'latitude',
                'value' => sprintf(':"%s";', $value),
                'compare' => 'LIKE'
            ),
            array(
                'key' => 'latitude',
                'value' => sprintf(';i:%d;', $value),
                'compare' => 'LIKE'
            )
        )
    );
Pablo SG Pacheco
sumber
REGEXPbagus dalam situasi tertentu, tetapi jika Anda dapat menggunakan LIKE, saya pikir itu metode yang lebih disukai. Tautan lama, tetapi masih cukup berguna, menurut saya: thingsilearn.wordpress.com/2008/02/28/… :-)
Erenor Paz
@ErenorPaz Anda benar. LIKElebih cepat. Tetapi ini adalah solusi yang bekerja untuk string dan angka
Pablo SG Pacheco
Ya..jadi, jawabannya adalah (seperti biasa): tergantung pada situasinya, jika Anda dapat menggunakan "LIKE"; itu lebih disukai, kalau tidak REGEXP akan melakukan juga :-)
Erenor Paz
@ErenorPaz, saya mengedit jawaban saya menambahkan solusi baru yang menggunakan LIKEtetapi berfungsi untuk angka dan string. Saya tidak yakin tentang kinerjanya karena harus membandingkan hasil menggunakanOR
Pablo SG Pacheco
Tepat !!! yang saya butuhkan untuk mendapatkan hasil yang sama seperti ini .... Terima kasih Man !!!
kuldip Makadiya
0

Setelah membaca banyak tips untuk menjalankan WP_Querypemfilteran dengan array berseri, inilah cara saya akhirnya melakukannya: dengan membuat array nilai yang dipisahkan koma menggunakan implode bersama dengan $wpdbkueri SQL kustom yang digunakan FIND_IN_SETuntuk mencari daftar yang dipisahkan koma untuk nilai yang diminta.

(ini mirip dengan jawaban Tomas, tetapi kinerjanya sedikit kurang intensif untuk query SQL)

1. Dalam functions.php:

Di file functions.php Anda (atau di mana pun Anda menyiapkan kotak meta) dalam penggunaan yourname_save_post()fungsi

update_post_meta($post->ID, 'checkboxArray', implode(",", $checkboxArray)); //adding the implode

untuk membuat array yang berisi nilai yang dipisahkan koma.

Anda juga ingin mengubah variabel output Anda di yourname_post_meta()fungsi konstruksi kotak meta admin menjadi

$checkboxArray = explode(",", get_post_custom($post->ID)["checkboxArray"][0]); //adding the explode

2. Dalam file PHP templat:

Tes: jika Anda menjalankan, get_post_meta( $id );Anda akan melihat checkboxArraysebagai array yang berisi nilai yang dipisahkan koma, bukan array serial.

Sekarang, kami membangun kueri SQL khusus kami menggunakan $wpdb.

global $wpdb;

$search = $post->ID;

$query = "SELECT * FROM wp_posts
          WHERE FIND_IN_SET( $search, (
              SELECT wp_postmeta.meta_value FROM wp_postmeta
              WHERE wp_postmeta.meta_key = 'blogLocations'
              AND wp_postmeta.post_id = wp_posts.ID )
          )
          AND ( wp_posts.post_type = 'post' )
          AND ( wp_posts.post_status = 'publish' );";

$posts = $wpdb->get_results($query);

foreach ($posts as $post) {
    //your post content here
}

Perhatikan FIND_IN_SET, di situlah keajaiban terjadi.

Sekarang ... karena saya menggunakan SELECT *ini mengembalikan semua data posting dan di dalam foreachAnda dapat menggemakan apa yang Anda inginkan dari itu (lakukan print_r($posts);jika Anda tidak tahu apa yang termasuk. Itu tidak mengatur "loop" untuk Anda (saya lebih suka seperti ini), tetapi dapat dengan mudah dimodifikasi untuk mengatur loop jika Anda mau (lihat di setup_postdata($post);dalam codex, Anda mungkin perlu mengubah SELECT *untuk memilih hanya memposting ID dan $wpdb->get_resultske $wpdbjenis yang benar - - melihat naskah kuno untuk $wpdbjuga untuk informasi tentang itu subjek).

Whelp, butuh sedikit usaha, tetapi karena wp_querytidak mendukung melakukan nilai 'compare' => 'IN'serial atau koma dipisahkan shim ini adalah pilihan terbaik Anda!

Semoga ini bisa membantu seseorang.

Gifford N.
sumber
0

Jika Anda menggunakan likeoperator pembanding dalam kueri meta Anda, itu akan berfungsi dengan baik untuk melihat ke dalam array serial.

$wp_user_search = new WP_User_Query(array(
    'meta_query' => array(
        array(
            'key'     => 'wp_capabilities',
            'value'   => 'subscriber',
            'compare' => 'not like'
            )
        )
    )
);

menghasilkan:

[query_where] => WHERE 1=1 AND (
  ( wp_usermeta.meta_key = 'wp_capabilities' 
  AND CAST(wp_usermeta.meta_value AS CHAR) NOT LIKE '%subscriber%' )
benklocek
sumber
0

Jika data meta saya adalah tipe array, saya menggunakan metode ini untuk kueri dengan meta:

$args = array(
    'post_type' => 'fotobank',
    'posts_per_page' => -1,
    'meta_query' => array(
            array(
                   'key' => 'collections',
                   'value' => ':"'.$post->ID.'";',
                   'compare' => 'LIKE'
            )
     )
);
$fotos = new WP_Query($args);
Den Media
sumber
Ini dapat menyebabkan hasil yang tidak diinginkan ketika ID posting memiliki nilai yang sama dengan id dari string serial
Erenor Paz
0

Saya jadi penasaran dengan jawaban di atas, di mana yang menjadi meta_querysasaran kunci latitudealih - alih _coordinates. Harus pergi dan menguji apakah benar-benar mungkin dalam meta queries untuk menargetkan kunci tertentu di dalam array serial. :)

Jelas bukan itu masalahnya.

Jadi, perhatikan bahwa kunci yang tepat untuk menargetkan _coordinatesbukan latitude.

$args = array(
     'post_type' => 'my-post-type',
     'meta_query' => array(
         array(
             'key' => '_coordinates',
             'value' => sprintf(':"%s";', $value),
             'compare' => 'LIKE'
         )
     )
 );

CATATAN:

  1. Pendekatan ini hanya memungkinkan untuk menargetkan pencocokan tepat. Jadi hal-hal seperti semua garis lintang lebih besar dari 50 tidak mungkin.

  2. Untuk memasukkan kecocokan substring, seseorang dapat menggunakan 'value' => sprintf(':"%%%s%%";', $value),. (belum diuji)

jgangso
sumber
-1

Saya punya pertanyaan yang sama. Mungkin Anda membutuhkan parameter 'type'? Lihat pertanyaan terkait ini: Kueri Bidang Kustom - Nilai Meta adalah Array

Mungkin mencoba:

    $ args = array (
    'post_type' => 'my-post-type',
    'meta_query' => array (
        array (
            'key' => 'latitude',
            'value' => '50',
            'bandingkan' => '>',
            'type' => 'numeric'
        )
    )
    );
pengguna4356
sumber
Terima kasih atas sarannya, tetapi ini bukan yang saya cari. Masalahnya adalah bahwa nilai yang saya coba cocokkan adalah bagian dari array yang diserialisasi dalam database.
tollmanz
Ya kamu benar. Saya mencoba ini pagi ini dan itu tidak berhasil untuk saya juga. Saya memiliki masalah yang sama. Menyimpan nilai kunci meta sebagai array. Saya mulai berpikir ini tidak dapat dilakukan dan saya mungkin malah harus menyimpannya sebagai bidang meta yang terpisah dengan nama yang sama ... dan hanya mengelola menghapus / memperbarui mereka dengan benar.
user4356
@ user4356 ... itulah yang akan saya lakukan. Saya berharap untuk mengurangi jumlah baris yang akan saya masukkan untuk setiap posting, tapi saya rasa itu tidak mungkin.
tollmanz
-1

Saya mengalami sesuatu yang serupa saat menggunakan plugin Magic Fields. Ini mungkin berhasil

$values_serialized = serialize(array('50'));
$args = array(
    'post_type' => 'my-post-type',
    'meta_query' => array(
        array(
            'key' => 'latitude',
            'value' => $values_serialized,
            'compare' => '>'
        )
    )
);
Seth Stevenson
sumber
1
Terima kasih untuk sarannya! Saya pikir ini sedekat yang bisa didapat, tapi itu tidak akan benar-benar berfungsi karena membandingkan array serial ke array serial lainnya tidak masuk akal kecuali saya sedang mencari pencocokan yang tepat.
tollmanz
5
Maka ini seharusnya tidak ditandai sebagai jawaban yang benar dan Anda tidak bertanggung jawab untuk melakukannya. Maka jawaban yang benar adalah 'Tidak, itu tidak mungkin'
Tom J Nowell
1
Setuju, juga WP menangani serialisasi untuk Anda, serialize()tidak diperlukan dalam hal ini ...
Adam
2
Sebenarnya @ seth-stevenson jawaban sangat bagus ketika melakukan apa yang dia katakan, menggunakan plugin "Magic Fields". Karena plugin tersebut membuat serial tipe data tertentu secara default, ini adalah cara terbaik untuk melakukan kecocokan EXACT.
zmonteca
@ TomJNowell Selesai! Hanya butuh waktu 5 bulan;)
tollmanz