Comment puis-je créer une meta_query avec un tableau comme meta_field?

16

Voici les arguments de ma requête:

$args = array(
    'post_type' => 'news',
    'meta_query' => array(
        array(
            'key' => 'topics',
            'value' => 'sports',
        )
    )
);

Cela fonctionne quand topicsest une chaîne, mais pas quand c'est un tableau. J'aimerais que cette requête fonctionne quand topicsest par exemplearray( 'sports', 'nonprofit', etc. )

Existe-t-il un moyen de créer des méta-requêtes avec des tableaux en tant que méta-clé?

mike23
la source
Veuillez clarifier - voulez-vous dire que la valeur stockée de "sujets" est un tableau? Ou que la valeur stockée est une chaîne et que vous souhaitez transmettre plusieurs termes à la requête dans un tableau?
MathSmath
@MathSmath, je veux dire que la valeur stockée est un tableau.
mike23

Réponses:

31

Alimenter la requête un tableau de valeurs possibles

Si la valeur dans la base de données est une chaîne et que vous souhaitez alimenter la requête plusieurs valeurs:

$args = array(
    'post_type' => 'news',
    'meta_query' => array(
        array(
            'key' => 'topics',
            'value' => array ( 'sports', 'nonprofit', 'community' ),
            'compare' => 'IN'
        )
    )
);

Recherche d'une valeur spécifique dans un tableau de données sérialisé

Si la valeur dans la base de données est un tableau de plusieurs rubriques et que vous souhaitez rechercher une seule rubrique dans ce tableau (notez qu'un tableau dans la base de données peut être récupéré en tant que tel, mais vit dans la base de données sous forme sérialisée, qui est un chaîne aussi):

$args = array(
    'post_type' => 'news',
    'meta_query' => array(
        array(
            'key' => 'topics',
            'value' => 'sports',
            'compare' => 'LIKE'
        )
    )
);

Utiliser 'LIKE' comme valeur de comparaison n'est pas une instruction aussi claire que vous l'auriez espéré, mais c'est la meilleure option.

À côté de cela, votre seule autre option serait de récupérer tous les messages qui ont les "sujets" meta_key définis et de les itérer manuellement ou, en d'autres termes, de vérifier la valeur dans la boucle et d'afficher les messages sur cette condition.

Johannes Pille
la source
14

Pour sortir de la réponse de Johannes, car il s'agit d'un tableau sérialisé, si vous stockez quelque chose comme l'ID utilisateur (ce qui était mon cas), vous devrez peut-être le gérer un peu différemment.

La méta du message était enregistrée comme:

array( "1", "23", "99");

Donc, oui, ce sont des entiers, mais update_post_metails étaient enregistrés sous forme de chaînes.

'meta_query' => array(
            array(
                    'key'     => 'my_meta_key',
                    'value'   => serialize( strval( 1 ) ),
                    'compare' => 'LIKE'
                )
            )

Vous effectuez donc une comparaison COMME avec la version de chaîne sérialisée de ce que vous recherchez. J'ai passé quelques bonnes heures à essayer de faire fonctionner quelque chose comme ça et jusqu'à présent, c'était le mieux que j'ai pu trouver.

sMyles
la source
serialize (strval (1)) a résolu mon problème, merci
Behzad
Je suis tombé sur cette vieille réponse par hasard aujourd'hui. J'aime bien ton ajout. +1
Johannes Pille
Je viens de découvrir cela aussi, mon truc, c'est que j'ai besoin d'obtenir tous les messages où user_id n'est pas dans le tableau, mais la solution ci-dessus ne fonctionne pas, alors je l'ai fait comme ceci: 'meta_query' => array( array( 'key' => 'my_meta_key', 'value' => ':' . $user_id . ';', 'compare' => 'NOT LIKE' ) ) Parce que quand sérialisé toutes les valeurs sont enregistrées comme: ' :valeur;'
Bobz
4

Une autre légère amélioration par rapport à la réponse de @sMyles.

J'ai eu des cas où les identifiants ont été stockés à la fois sous forme de chaînes (comme lorsqu'ils sont extraits d'une entrée de formulaire) et sous forme d'entiers (par exemple update_post_meta($post_id, authorized_users', array(get_current_user_id()));). C'est un peu comme le problème bien connu avec wp_set_object_terms()lequel vous pouvez utiliser des ID de terme pour définir les termes, mais si vous ne les convertissez pas en entiers, vous avez environ 50% de chances de créer de nouveaux termes avec ces nombres comme noms. au lieu.

Cela peut entraîner leur stockage très différent dans un tableau sérialisé, comme le montrent les extraits d'un tel cas de la base de données de mon site de test:

a:1:{i:0;s:1:"1";} // 's' for 'string', also note the double quotes
a:1:{i:0;i:1;} // 'i' for 'integer', no quotes

Les deux ci-dessus, une fois alimentés print_r(), rendront

Array
(
    [0] => 1
)

Pour résoudre ce problème, j'ai apporté une légère modification à l' meta_queryajout d'une relationet d' une autre version de la requête qui convertit la valeur en entier au lieu d'une chaîne.

Voici le résultat final:

        'meta_query' => array(
            'relation' => 'OR', // Lets it know that either of the following is acceptable
            array(
                'key' => 'bcm_enm_authorized_users',
                'value'   => serialize(strval(get_current_user_id())), // Saved as string
                'compare' => 'LIKE'
            ),
            array(
                'key' => 'bcm_enm_authorized_users',
                'value'   => serialize(intval(get_current_user_id())), // Saved as integer
                'compare' => 'LIKE'
            ),
        ),

EDIT: Je viens de réaliser que cette méthode pouvait courir le risque de collisions avec des index de tableau, ce qui pourrait permettre à quelqu'un d'accéder illégalement aux matériaux s'ils ne sont pas dans le tableau, mais leur ID utilisateur apparaît comme un index. En tant que tel, bien que cela fonctionne si le problème est discuté, la meilleure pratique consiste à s'assurer que toutes les valeurs que vous souhaitez rechercher sont converties en chaînes avant de les enregistrer afin que vous puissiez utiliser la méthode @sMyles à la place.

Kaji
la source
Cela devrait être la réponse choisie, la plus fiable
Amin
2

J'irais pour la réponse de Johannes. Cependant, je veux améliorer cela, car en utilisant cette méta-requête, vous rencontrerez un cas comme celui-ci

votre valeur est

array('sports','movies', 'sports2');

quand tu cherches

$args = array(
    'post_type' => 'news',
    'meta_query' => array(
        array(
            'key' => 'topics',
            'value' => 'sports',
            'compare' => 'LIKE'
        )
    )
);

le résultat renverra alors à la fois «sport» et «sport2».

Pour résoudre ce problème, changez les arguments meta_query en

$args = array(
    'post_type' => 'news',
    'meta_query' => array(
        array(
            'key' => 'topics',
            'value' => 'sports";',
            'compare' => 'LIKE'
        )
    )
);

C'est parce que la valeur est sérialisée dans la base de données, et chaque élément sera séparé par un point-virgule. Ainsi, les arguments ci-dessus fonctionneront

Si les éléments de la valeur sont des nombres, il vous suffit de supprimer le double guillemet "

$args = array(
        'post_type' => 'news',
        'meta_query' => array(
            array(
                'key' => 'topics',
                'value' => '1;',
                'compare' => 'LIKE'
            )
        )
    );
Ha Doan Ngoc
la source
1

J'ai lutté avec quelque chose de similaire aujourd'hui. Je dois interroger un champ de relation ACF (Advanced Custom Fields) avec plusieurs utilisateurs associés (tableau).

Après la mise à jour du champ via php, la requête n'a pas fonctionné. Après l'avoir mis à jour via l'interface ACF, la requête a fonctionné.

Le problème était que mon code php définissait les valeurs de la relation comme des valeurs int, l'interface utilisateur le définissait comme des valeurs de chaîne. Pour m'assurer que les deux fonctionnent, j'utilise maintenant cette requête (adaptée à l'exemple ici):

$args = array(
    'post_type' => 'news',
    'meta_query' => array(
        'relation' => 'OR',
        array(
            'key' => 'topics',
            'value' => '1;',  // works for int-array
            'compare' => 'LIKE'
        ),
        array(
            'key' => 'topics',
            'value' => '"1"',  // works for string-array
            'compare' => 'LIKE'
        ),
    )
);
Julian Stark
la source