Orderby meta_value ne renvoie que les messages pour lesquels meta_key existe

10

J'ai le wp_query suivant:

$args = array(
    'post_type' => 'news',
    'orderby' => 'meta_key',
    'order' => 'ASC',
    'meta_key'=>'custom_author_name',
    'post_per_page'=>-1
);

$query = new WP_Query($args);

echo $query->found_posts;

echo = 10 résultats car il n'y a que 10 news posts avec un meta_key = custom_author_name . Mais il y a des centaines de news posts qui n'ont pas de ligne post_meta avec cette meta_key spécifique. Veuillez noter qu'il n'y a pas de méta_query impliquée. Aucune méta_value n'est attribuée, car j'essaie uniquement de trier les publications par méta_key, et non de filtrer par meta_value.

Ne devrait-il pas commander en sélectionnant tous les articles? et juste les commander?

Si oui, pourquoi le résultat est-il filtré? Si la méta_key n’est pas trouvée, pourquoi ne pas utiliser une chaîne vide ou une correspondance avec tous?

Si non, pourquoi pas?

Si je saisis une méta_key pour chaque message (même s'il s'agit d'une chaîne vide), le résultat attendu est obtenu. Mais cela semble être un tas de lignes de table qui n’ont pas besoin d’être là.

    
posée gdaniel 14.05.2015 - 01:14

4 réponses

9

Comme indiqué dans la réponse de @ ambroseya, son fonctionnement est supposé fonctionner de la sorte. Une fois que vous déclarez une méta-requête, même si vous ne recherchez pas une valeur spécifique, il ne fera que rechercher les publications avec cette clé méta déclarée. Si vous souhaitez inclure toutes les publications, triez-les à l'aide de la clé méta, utilisez le code suivant:

$args = array(
    'post_type' => 'news',
    'orderby' => 'meta_key',
    'order' => 'ASC',
    'meta_query' => array(
        'relation' => 'OR',
        array( 
            'key'=>'custom_author_name',
            'compare' => 'EXISTS'           
        ),
        array( 
            'key'=>'custom_author_name',
            'compare' => 'NOT EXISTS'           
        )
    ),
    'post_per_page'=>-1
);

$query = new WP_Query($args);

echo $query->found_posts;

Ceci utilise une méta-requête avancée qui recherche les publications qui ont et n'ont pas cette méta-clé déclarée. Puisque celui avec EXISTS est le premier, lorsque vous triez par meta_value , la première requête est utilisée.

    
réponse donnée Manny Fleurmond 14.05.2015 - 04:41
1

C'est comme ça que ça fonctionne.

Si vous souhaitez le faire sans ajouter de rangées de table, vous devez effectuer deux requêtes. Une avec la meta_key qui a les résultats limités et l'autre qui obtient la liste complète; Ensuite, utilisez PHP pour comparer les deux résultats de la requête (en supprimant éventuellement les résultats meta_key de l’autre requête pour supprimer les doublons, ou tout ce qui a du sens dans votre paramètre).

    
réponse donnée ambroseya 14.05.2015 - 03:45
0

Malheureusement, ce n'est pas comme ça que WP_Query fonctionne. Dès que vous ajoutez ce composant "méta", vous avez créé une sorte de filtre. Dump $query->request et vous verrez ce que je veux dire.

Deuxièmement, WP_Query ne prend pas en charge la commande par une méta clé . Vous pouvez commander par une meta valeur pour une clé particulière mais non par la clé elle-même. Encore une fois, videz la requête pour voir ce que je veux dire. Vous remarquerez que les composants "ordre" sont abandonnés si vous essayez.

À mon avis, le moyen le plus propre de faire en sorte que cela fonctionne consiste à utiliser deux filtres courts:

function join_meta_wpse_188287($join) {
  remove_filter('posts_join','join_meta_wpse_188287');
  global $wpdb;
  return ' INNER JOIN '.$wpdb->postmeta.' ON ('.$wpdb->posts.'.ID = '.$wpdb->postmeta.'.post_id)';
}
add_filter('posts_join','join_meta_wpse_188287');

function orderby_meta_wpse_188287($orderby) {
  remove_filter('posts_orderby','orderby_meta_wpse_188287');
  global $wpdb;
  return $wpdb->postmeta.'.meta_key ASC';
}
add_filter('posts_orderby','orderby_meta_wpse_188287');

$args = array(
    'post_type' => 'news',
    'post_per_page'=>-1
);
$q = new WP_Query($args);
var_dump($q->request); // debug
var_dump(wp_list_pluck($q->posts,'post_title')); // debug
    
réponse donnée s_ha_dum 14.05.2015 - 15:41
0

J'ai essayé d'appliquer la réponse de @Manny Fleurmond et, comme @Jake, je ne pouvais pas la faire fonctionner même après avoir corrigé la faute de frappe voulant que 'orderby' => 'meta_key' soit 'orderby' => 'meta_value' . (Et pour être complet, ce devrait être 'posts_per_page' et non pas 'post_per_page' , mais cela n’affectera pas le problème à l’étude.)

Si vous regardez la requête SQL générée par la réponse de @Manny Fleurmond (après avoir corrigé les fautes de frappe), voici ce que vous obtenez:

SELECT   wp_{prefix}_posts.* FROM wp_{prefix}_posts
LEFT JOIN wp_{prefix}_postmeta ON (wp_{prefix}_posts.ID = wp_{prefix}_postmeta.post_id AND wp_{prefix}_postmeta.meta_key = 'custom_author_name' )
LEFT JOIN wp_{prefix}_postmeta AS mt1 ON ( wp_{prefix}_posts.ID = mt1.post_id )
WHERE 1=1  AND ( 
    wp_{prefix}_postmeta.post_id IS NULL 
    OR 
    mt1.meta_key = 'custom_author_name'
) AND wp_{prefix}_posts.post_type = 'news' AND
(wp_{prefix}_posts.post_status = 'publish' OR wp_{prefix}_posts.post_author = 1 AND wp_{prefix}_posts.post_status = 'private')
GROUP BY wp_{prefix}_posts.ID ORDER BY wp_{prefix}_postmeta.meta_value ASC

Ceci illustre la manière dont WP analyse les vars de requête: il crée une table pour chaque clause meta_query, puis détermine comment les associer et comment commander. La commande fonctionnerait correctement si vous n’utilisiez qu’une seule clause avec 'compare' => 'EXISTS' , mais en joignant la seconde clause 'compare' => 'NOT EXISTS' avec OR (comme il se doit), la commande est gâchée. Le résultat est que LEFT JOIN est utilisé pour joindre la première clause / table et la seconde clause / table - et la façon dont WP assemble tous les éléments signifie que la table créée à l'aide de 'compare' => 'EXISTS' est actuellement renseignée avec les méta_values de TOUT champ personnalisé, pas seulement le champ 'custom_author_name' qui nous intéresse. Je pense donc que le classement par cette clause / table ne donnera les résultats souhaités que si le post_type de 'news' ne comporte qu'un seul champ personnalisé.

La solution qui a fonctionné dans mon cas a été de passer commande par l’autre clause / table - celle de NOT EXISTS. En apparence contre-intuitif, je le sais, mais à cause de la façon dont WP analyse les vars de requête, c’est dans cette table que meta_value est rempli uniquement par le champ personnalisé que nous recherchons.

(La seule façon dont j'ai compris cela était en exécutant l'équivalent de cette requête pour mon cas:

SELECT   wp_{prefix}_posts.ID, wp_{prefix}_postmeta.meta_value, mt1.meta_value FROM wp_{prefix}_posts
LEFT JOIN wp_{prefix}_postmeta ON (wp_{prefix}_posts.ID = wp_{prefix}_postmeta.post_id AND wp_{prefix}_postmeta.meta_key = 'custom_author_name' )
LEFT JOIN wp_{prefix}_postmeta AS mt1 ON ( wp_{prefix}_posts.ID = mt1.post_id )
WHERE 1=1  AND ( 
    wp_{prefix}_postmeta.post_id IS NULL 
    OR 
    mt1.meta_key = 'custom_author_name'
) AND wp_{prefix}_posts.post_type = 'news' AND
(wp_{prefix}_posts.post_status = 'publish' OR wp_{prefix}_posts.post_author = 1 AND wp_{prefix}_posts.post_status = 'private')
ORDER BY wp_{prefix}_postmeta.meta_value ASC

Tout ce que j'ai fait est de changer les colonnes affichées et de supprimer la clause GROUP BY. Cela m’a ensuite montré ce qui se passait - que la colonne postmeta.meta_value récupérait les valeurs de tous les meta_keys, tandis que la colonne mt1.meta_value n’extrayait que les méta_valeurs du champ personnalisé news.)

La solution

Comme le dit @Manny Fleurmond, c'est la première clause utilisée pour la commande, la solution consiste donc simplement à échanger les clauses, en donnant ceci:

$args = array(
    'post_type' => 'news',
    'orderby' => 'meta_value',
    'order' => 'ASC',
    'meta_query' => array(
        'relation' => 'OR',
        array( 
            'key' => 'custom_author_name',
            'compare' => 'NOT EXISTS'           
        ),
        array( 
            'key' => 'custom_author_name',
            'compare' => 'EXISTS'           
        )
    ),
    'posts_per_page' => -1
);

$query = new WP_Query($args);

Vous pouvez également créer des clauses de tableaux associatifs et les classer par la clé correspondante, comme suit:

$args = array(
    'post_type' => 'news',
    'orderby' => 'not_exists_clause',
    'order' => 'ASC',
    'meta_query' => array(
        'relation' => 'OR',
        'exists_clause' => array( 
            'key' => 'custom_author_name',
            'compare' => 'EXISTS'           
        ),
        'not_exists_clause' => array( 
            'key' => 'custom_author_name',
            'compare' => 'NOT EXISTS'           
        )
    ),
    'posts_per_page' => -1
);

$query = new WP_Query($args);
    
réponse donnée jlad26 05.11.2017 - 10:09

Lire d'autres questions sur les étiquettes