serileştirme dizileri olarak meta değerleri olan meta_query


37

Özel yazı türümü ve özel yazı türümle ilişkili meta kutuları üzerinden girilen özel verileri oluşturduğum bir proje üzerinde çalışıyorum. Her ne sebeple olursa olsun, meta kutularını her meta kutudaki girdilerin bir dizinin parçası olacak şekilde kodlamaya karar verdim. Mesela, enlem ve boylamı saklıyorum:

<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>

Sebep ne olursa olsun, her bir meta kutu için tekil bir postmeta girişi yapma fikrini sevdim. Açık save_postkanca, Veri yüzden mi kaydedin:

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

Bunu yaptım çünkü üç meta kutum var ve her yazı için sadece 3 postmeta değerine sahip olmayı seviyorum; ancak şimdi bununla ilgili potansiyel bir sorun fark ettim. WP_Query'yi bu meta değerlerini temel alan yalnızca belirli gönderileri çıkarmak için kullanmak isteyebilirim. Örneğin, enlem değerleri 50'nin üstünde olan tüm gönderileri almak isteyebilirim. Bu verileri veritabanında ayrı ayrı, belki de anahtarı kullanarak latitudeolsaydım, şöyle bir şey yapardım:

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

_coordinatesPostmetanın bir parçası olarak enlemim olduğu için bu işe yaramaz.

Öyleyse benim sorum şu, meta_querybu senaryoda benimki gibi serileştirilmiş bir diziyi sorgulamanın bir yolu var mı?

Yanıtlar:


37

Hayır, mümkün değil ve hatta tehlikeli olabilir.

Verilerinizi seri hale getirmenizi ve kaydetme yordamınızı değiştirmenizi şiddetle öneririz. Buna benzer bir şey verilerinizi yeni formata dönüştürmelidir:

$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);
    }
}

Ardından, tek tek tuşlarla istediğiniz gibi sorgulayabilirsiniz

Birden fazla boylam ve birden fazla enlem saklamanız gerekiyorsa, aynı ada sahip birden fazla posta metaini saklayabilirsiniz. Basitçe üçüncü parametresini kullanın ve get_post_metahepsini bir dizi olarak döndürür.

Neden Serileştirilmiş Verilerin İçinde Sorgulayamıyorsunuz?

MySQL onu sadece bir dize olarak görür ve yapılandırılmış veriye ayıramaz. Bunu yapılandırılmış veriye bölmek, yukarıdaki kodun yaptığı gibi.

Tarihin kısmi bölümlerini sorgulayabilirsiniz, ancak bu çok sayıda güvenilmez, çok güvenilmez, pahalı, yavaş ve çok kırılgan olacaktır. Serileştirilmiş veriler, SQL sorguları için tasarlanmamıştır ve düzenli ve sabit bir şekilde biçimlendirilmez.

Kısmi dize aramaların maliyetlerinin yanı sıra, meta sonrası sorgulamalar yavaş ve aradığınız değere bağlı olarak imkansız olmasa da, içeriğin uzunluğu gibi şeylere bağlı olarak seri hale getirilmiş veriler değişebilir

Kayıtları / Varlıkları / Nesneleri Meta'da Sıralanmış Nesneler Olarak Saklamak Üzerine Bir Not

Bir işlem kaydını post metada veya user meta'de başka bir tür veri yapısını depolamak isteyebilir ve daha sonra yukarıdaki sorunu çözebilirsiniz.

Buradaki çözüm, bireysel post metaya bölmek değil, başlangıçta asla meta olması gerekmediğini, ancak özel bir post tipinin olması gerektiğidir. Örneğin, bir günlük veya kayıt özel bir gönderi türü olabilir, orijinal gönderi ebeveyn olarak veya bir taksonomi terimi ile birleştirilebilir.

Güvenlik ve Seri Nesneler

Serileştirilmiş PHP nesnelerini serializeişlev aracılığıyla saklamak tehlikeli olabilir ; bu, bir nesneyi WordPress'e iletmek, bunun serileştirildiği anlamına geleceği için talihsiz olabilir . Bunun nedeni, nesnenin serileştirilmesi, bir nesnenin yaratılması ve tüm uyandırma yöntemleri ve yapıcılarının çalıştırılmasıdır. Bir kullanıcı dikkatle hazırlanmış bir giriş gizlice girmeyi başaramazsa, veri veritabanından okunduğunda ve WordPress tarafından seri hale getirildiğinde uzaktan kod yürütülmesine yol açana kadar bu büyük bir sorun gibi görünmeyebilir.

Bunun yerine JSON kullanmaktan kaçınılabilir, bu da sorguları kolaylaştırır, ancak yalnızca verileri doğru şekilde depolamak ve yapılandırılmış serileştirilmiş verilerin başlamasını önlemek için çok daha kolay / daha hızlıdır.


5
Geçen insanlar için, okumayı
bırakma

Kaydedilecek bir ID dizisine sahipsem - ve bunların her biri 'enlem' vb. Altında kaydedebileceğim farklı bir anahtarı temsil etmiyorsa, herkes için sadece bir anahtardır (ilişkileri kaydederken olduğu gibi). Ne yapmalı o zaman? @ rabni'nin çözümü?
trainoasis

1
Bir anahtarı bir kereden fazla saklayabilirsiniz, anahtar değer çiftleri benzersiz değildir. İlişkilere gelince, taksonomiler bunun için var, eğer bir şeyi birden çok şeyle eşleştirmek için meta kullanıyorsanız, bunun yerine onları taksonomi terimine koyun
Tom J Nowell

24

Ben de bu duruma rastladım. İşte yaptığım şey:

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

Umarım bu yardım


1
Bu çözümü gerçekten beğendim. Ne yazık ki, bu $valueaynı zamanda bir kimlik olduğunda geçerli değildir . Bu durumda, verileri kaydetmeden önce her dizi elemanına karakter eklemek için işlevler ve verileri kullanmadan önce karakteri kaldırmak için başka bir işlev oluşturmanızı öneririm. Bu wat, serileştirilmiş i:2endeks i:D2"gerçek" verilerle karıştırılmayacak. Daha sonra meta sorgu parametresi olmalı 'value' => sprintf(':"D%s";', $value),ve bu harika cevabın doğru işlevselliğini koruyacaksınız!
Erenor Paz

Bu çözüm benim için çalışıyor
Vishal

Bu da benim için mükemmel çalıştı. Kabul edilen çözümü gördüğümde küçük bir panik var mıydı
Shane Jones

@Erenor Paz, hem ID hem de Strings ile iyi çalışan bir çözüm yolladım: wordpress.stackexchange.com/a/299325/25264
Pablo SG Pacheco

kullanarak LIKEsunucunuzu aşağı indirmenin harika ve hızlı bir yoludur (yanlış pozitiflerden bahsetmiyorum) daha iyi bir önbelleklemeye sahip olmalısınız.
Mark Kaplun

10

Verileri WP veritabanına serileştirirken verimli bir şekilde sorgulama yeteneğinizi kaybedeceksiniz.

Genel performans tasarrufu ve seri hale getirme ile kazandığınızı düşündüğünüz kazanç, büyük ölçüde farkedilmeyecek. Biraz daha küçük bir veritabanı boyutu elde edebilirsiniz, ancak bu alanları sorgularsanız ve bunları yararlı, anlamlı bir şekilde karşılaştırmaya çalışırsanız, SQL işlemlerinin maliyeti ağır olacaktır.

Bunun yerine, sorgulamayı istemediğiniz veriler için serileştirmeyi kaydedin, ancak bunun yerine yalnızca doğrudan WP API çağrısı ile pasif bir şekilde erişirsiniz; get_post_meta()bu işlevden dizi özelliklerine erişmek için serileştirilmiş bir girişi de açabilirsiniz.

Gerçekte, olduğu gibi doğru değer atanmış ;

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

Verileri, normal başına yineleme yapabilmeniz için erişilebilir bir dizi olarak döndürür.

Önbelleğe alma, CSS ve JS minification gibi diğer veritabanı / site optimizasyonlarına ve gerekirse bu servisleri CDN olarak kullanmaya odaklanabilirsiniz. Birkaç isim ama .... WordPress Codex bu konuda daha fazlasını ortaya çıkarmak için iyi bir başlangıç ​​noktasıdır: HERE


3

Sadece serileştirilmiş alanlarla uğraştım ve sorgulayabiliyorum. Meta_query kullanarak değil, bir SQL sorgusu kullanarak.

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);

Sorgu ilk önce post_type ile eşleşen yazıyı arar, böylece wp_postmeta kayıtlarının miktarı filtrelemek için daha az olacaktır. Sonra filtrelemek suretiyle satırları daha da azaltmak için bir where ifadesi ekledim.meta_key

Kimlikler, get_posts için gerektiği gibi bir dizide güzelce sonuçlanır.

PS. İyi sorgulama performansı için MySQL v5.6 veya daha üstü gerekir


1

Bu örnek bana gerçekten yardımcı oldu. Özellikle (kullanıcı meta verilerini seri hale getiren) S2Members eklentisi için. Ancak meta_key içindeki serileştirilmiş bir dizinin bir kısmını sorgulamanıza izin verir.

MySQL REGEXP işlevini kullanarak çalışır.

İşte kaynak

ABD'de yaşayan tüm kullanıcıları sorgulayan kod burada. Özel kayıt alanlarımdan birini sorgulamak için kolayca değiştirdim ve kısa sürede çalışmasını sağladım.

  <?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. */
            }
    }
?>

1

Hem String hem de Integers olarak saklanan sonuç problemini çözmeyi deneyebilecek 2 çözüm olduğunu düşünüyorum. Bununla birlikte, diğerleri tarafından belirtildiği gibi, Integer olarak saklanan sonuçların bütünlüğünü garanti etmenin mümkün olmadığını söylemek önemlidir, çünkü serileştirilmiş diziler olarak depolanan bu değerler gibi, dizin ve değerler aynı desende saklanır. Örnek:

array(37,87);

Bu gibi seri hale getirilmiş bir dizi olarak saklanır

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

Not i:0dizinin ilk pozisyon olarak ve i:37ilk değeri. Desen aynıdır. Ama hadi çözümlere gidelim


1) REGEXP Çözümü

Bu çözüm benim için string veya number / id olarak kaydedilen meta değerinden bağımsız olarak çalışır. Ancak kullandığı REGEXPkadar hızlı olmayan kullanırLIKE

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

2) GİBİ Çözüm

Performans farkından emin değilim ama bu LIKEsayı ve karakter dizileri için de kullanan ve aynı zamanda çalışan bir çözüm.

 $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'
            )
        )
    );

REGEXPBazı durumlarda hoş, ama kullanabiliyorsan LIKE, tercih edebileceğim bir yöntem olduğunu düşünüyorum. Eski bir bağlantı, ama yine de oldukça faydalı, bence: thingsilearn.wordpress.com/2008/02/28/… :-)
Erenor Paz

@ErenorPaz Haklısınız. LIKEdaha hızlı. Fakat bu, hem dizgiler hem de sayılar için işe yarayan bir çözümdür
Pablo SG Pacheco

Evet, yani cevap (her zamanki gibi): duruma göre, "LIKE" kullanabilirseniz; tercih edilir, aksi takdirde
REGEXP

@ErenorPaz, cevabımı LIKEhem sayı hem de dizgede kullanılan ancak çalışan yeni bir çözüm ekleyerek düzenledim . Performans hakkında emin değilim çünkü sonuçları kullanarak karşılaştırmak zorunda.OR
Pablo SG Pacheco

Kesinlikle !!! Bu aynı sonucu elde etmek gerekir hangi .... Teşekkürler Man !!!
kuldip Makadiya

0

WP_QuerySerileştirilmiş dizilerle filtrelemeyi çalıştırmak için birkaç ipucu okuduktan sonra , işte nihayet yaptım: virgülle ayrılmış değerler dizisini , istenen değer için virgülle ayrılmış listede arama yapmak için $wpdbkullanılan özel bir SQL sorgusu ile birlikte kullanarak kullanarak FIND_IN_SET.

(Bu Tomas'un cevabına benzer, ancak SQL sorgusu için biraz daha az performanslıdır)

1. functions.php içinde:

Function.php dosyanızda (veya meta kutusunu kurduğunuz her yerde) yourname_save_post()işlev kullanımında

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

virgülle ayrılmış değerleri içeren bir dizi oluşturmak için.

Ayrıca yourname_post_meta()yönetici meta kutusu inşaatı işlevindeki çıktı değişkeninizi de değiştirmek isteyeceksiniz .

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

2. Şablon PHP dosyasında:

Test: Eğer bir çalıştırırsanız , seri hale getirilmiş bir dizi yerine virgülle ayrılmış değerleri içeren bir dizi olarak get_post_meta( $id );görmelisiniz checkboxArray.

Şimdi, özel SQL sorgumuzu kullanarak inşa ediyoruz $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
}

Dikkat, FIND_IN_SETbüyünün olduğu yer burası.

Şimdi ... Bunu kullanmaya başladığımdan beri tüm gönderi verileriniSELECT * döndürür ve içinde ne istediğinizi tekrarlayabilirsiniz (ne dahil olduğunu bilmiyorsanız bir yapın. Bunun için "döngü" yi kurmaz. siz (bu şekilde tercih ederim), ancak tercih ederseniz döngüyü ayarlamak için kolayca değiştirilebilir ( kodeksi inceleyin, muhtemelen yalnızca posta kimliklerini seçmek ve doğru türe seçmek için değiştirmeniz gerekecektir - - bu konudaki bilgiler için de kodeksi'ne bakınız ).foreachprint_r($posts);setup_postdata($post);SELECT *$wpdb->get_results$wpdb$wpdb

Biraz çaba sarf etti ama serileştirilmiş ya da virgülle ayrılmış değerler wp_queryyapmayı desteklemediğinden 'compare' => 'IN'bu şim en iyi seçenek!

Umarım bu birine yardımcı olur.


0

Eğer kullanırsanız likemeta sorguda karşılaştırma operatörü, bir tefrika diziye içine bakmak için para cezası çalışmalıdır.

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

sonuçlanır:

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

0

Meta verilerim dizi türüyse, meta yöntemi ile sorgulamak için bu yöntemi kullanıyorum:

$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);

Bir yayın kimliği serileştirilmiş dizgenin kimliğiyle aynı değerde olduğunda istenmeyen sonuçlara yol açabilir
Erenor Paz

0

Yukarıdaki cevapları merak ettim, meta_queryhedef latitudeyerine anahtarı hedef aldım _coordinates. Serileştirilmiş bir dizi içinde belirli bir anahtarı hedeflemenin meta sorgularında gerçekten mümkün olup olmadığını test etmek zorunda kaldım. :)

Açıkçası durum böyle değildi.

Bu nedenle, hedeflenecek doğru anahtarın _coordinatesyerine olduğuna dikkat edin latitude.

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

NOTLAR:

  1. Bu yaklaşım, yalnızca tam eşleşmeleri hedeflemeyi mümkün kılar. Bu yüzden 50'den büyük tüm enlemler gibi şeyler mümkün değildir.

  2. Alt dize eşleşmelerini dahil etmek için biri kullanılabilir 'value' => sprintf(':"%%%s%%";', $value),. (test edilmedi)


-1

Ben de aynı sorum var. Belki 'type' parametresine ihtiyacınız vardır? Bu ilgili soruya göz atın: Özel Alan Sorgusu - Meta Değer Array

Belki dene:

    $ args = dizi (
    'post_type' => 'Yazım tipim'
    'meta_query' => dizi (
        dizi(
            'key' => 'enlem'
            'değer' => '50'
            'compare' => '>'
            'type' => 'sayısal'
        )
    )
    );

Öneri için teşekkürler, ama benim peşimde olan bu değil. Sorun, eşleştirmeye çalıştığım değerin veritabanında serileştirilmiş bir dizinin parçası olmasıdır.
tollmanz

Evet, haklısın. Bunu bu sabah denedim ve bu benim için de işe yaramadı. Bende de aynı sorun var. Bir meta anahtarının değerini dizi olarak saklamak. Bunun yapılamayacağını düşünmeye başlıyorum ve bunun yerine onları aynı adla ayrı meta alanlar olarak depolamak zorunda kalabilirim ... ve sadece onları silme / güncelleme işlemlerini yönetebilirim.
user4356,

@ user4356 ... tam olarak ne yapacağım. Her yazı için ekleyeceğim satır sayısını azaltmayı umuyordum, ama sanırım bu mümkün değil.
tollmanz

-1

Magic Fields eklentisini kullanırken benzer bir şeyle karşılaştım. Bu hile yapabilir

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

1
Önerin için teşekkürler! Sanırım bu bir kişinin alabileceği kadar yakın, ancak seri hale getirilmiş bir diziyi başka bir dizileştirilmiş diziyle karşılaştırmak, tam bir eşleşme aramamadıkça bir anlam ifade etmiyor.
tollmanz

5
Öyleyse bu doğru cevap olarak işaretlenmemelidir ve bunu yapmak sizin sorumluluğunuzda değildir. Bu yüzden doğru cevap 'Hayır, mümkün değil' olacaktır
Tom J Nowell

1
Kabul ediyorum, ayrıca WP sizin için seri hale getirme işlemlerini gerçekleştiriyor, serialize()bu durumda gerekli değil ...
Adam

2
Aslında @ seth-stevenson'ın yanıtı, "Magic Fields" eklentisini kullanarak tam olarak ne söylediğini yaparken çok iyi. Bu eklenti belirli veri türlerini varsayılan olarak seri hale getirdiğinden, bu bir EXACT eşleşmesi yapmanın en iyi yoludur.
zmonteca

@ TomJNowell Tamam! Sadece bana 5 ay sürdü;)
tollmanz
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.