Orderby meta_value ne renvoie que les publications qui ont une meta_key existante

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 newsmessages avec a meta_key = custom_author_name. Mais il y a des centaines de newspublications qui n'ont pas de ligne post_meta avec cette meta_key spécifique. Veuillez noter qu'aucune méta-requête n'est impliquée. Aucune méta_value n'est affectée, car j'essaie uniquement de trier les messages par meta_key, et non de filtrer par meta_value.

Ne devrait pas commander en sélectionnant tous les messages? et juste les commander?

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

Sinon, pourquoi pas?

Si j'entre une méta-clé dans chaque article (même s'il s'agit d'une chaîne vide), j'obtiens le résultat attendu. Mais cela semble être un tas de lignes de tableau qui n'ont pas besoin d'être là.

gdaniel
la source

Réponses:

10

Comme indiqué dans la réponse de @ ambroseya, son censé fonctionner comme ça. Une fois que vous déclarez une méta-requête, même si vous ne recherchez pas de valeur spécifique, elle ne questionnera que les publications avec cette méta-clé déclarée. Si vous souhaitez inclure tous les articles, triez-les par la clé méta, utilisez le code suivant:

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

$query = new WP_Query($args);

echo $query->found_posts;

Ce que cela fait, c'est utiliser une méta-requête avancée qui recherche les publications qui ont et n'ont pas cette clé de méta déclarée. Étant donné que celui avec EXISTSest le premier, lorsque vous triez meta_value, il utilisera la première requête.

Manny Fleurmond
la source
3
Cela n'a pas du tout changé l'ordre pour moi, quand j'ai utilisé 'orderby' => 'meta_value', cela a changé l'ordre, mais cela n'avait rien à voir avec le champ méta réel.
Jake
3

J'ai essayé d'appliquer la réponse de @Manny Fleurmond et comme @Jake, je n'ai pas pu le faire fonctionner même après avoir corrigé la faute de frappe qui 'orderby' => 'meta_key'devrait être 'orderby' => 'meta_value'. (Et pour être complet, cela ne devrait 'posts_per_page'pas l' être, 'post_per_page'mais cela n'affecte pas le problème examiné.)

Si vous regardez la requête SQL réellement 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

Cela illustre la façon dont WP analyse les variables de requête: il crée une table pour chaque clause meta_query, puis détermine comment les joindre et comment commander. La commande fonctionnerait bien si vous n'utilisiez qu'une seule clause avec 'compare' => 'EXISTS', mais joindre la deuxième 'compare' => 'NOT EXISTS'clause avec OR (comme nous devons) gâche la commande. Le résultat est que LEFT JOIN est utilisé pour joindre à la fois la première clause / table et la seconde clause / table - et la façon dont WP met tout ensemble signifie que la table créée à l'aide 'compare' => 'EXISTS'est réellement remplie avec des méta-valeurs de N'IMPORTE QUEL champ personnalisé, pas seulement le 'custom_author_name'champ qui nous intéresse. Je pense donc que la commande par cette clause / table ne donnera les résultats souhaités que si le post_type particulier de 'news' n'a qu'un seul champ personnalisé.

La solution qui a fonctionné pour ma situation a été de commander par l'autre clause / table - celle PAS EXISTE. Apparemment contre-intuitif, je sais, mais en raison de la façon dont WP analyse les variables de requête, c'est cette table qui meta_valuen'est remplie que par le champ personnalisé que nous recherchons.

(La seule façon dont j'ai compris cela était d'exécuter 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 modifier 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 toutes les méta-clés, tandis que la colonne mt1.meta_value récupérait uniquement les méta-valeurs du champ personnalisé de news.)

La solution

Comme le dit @Manny Fleurmond, c'est la première clause qui est utilisée pour la commande, donc la réponse est juste d'é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);

Alternativement, vous pouvez rendre les clauses des tableaux associatifs et les trier par la clé correspondante, comme ceci:

$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);
jlad26
la source
Il convient de souligner que si la méta-clé custom_author_nameest définie puis désactivée, cela meta_keyrépondra EXISTSet l'effet sera que ceux-ci seront propagés aux côtés des messages qui ont un custom_author_name. Dans mon cas, j'ai une case à cocher, donc j'utilise à la "value" => "1"place de EXISTS, mais les chaînes auront besoin d'une approche différente.
djb
1

Voilà comment cela fonctionne.

Si vous souhaitez le faire sans ajouter de lignes de tableau, vous devrez effectuer deux requêtes. Un avec la meta_key qui a les résultats limités, et l'autre qui obtient la liste entière; puis 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).

ambroseya
la source
0

Malheureusement, ce n'est pas ainsi que cela WP_Queryfonctionne. Dès que vous ajoutez ce composant "meta", vous avez créé une sorte de filtre. Vider $query->requestet vous verrez ce que je veux dire.

Deuxièmement, WP_Queryne prend pas du tout en charge la commande par une clé méta . Vous pouvez commander par une méta- valeur pour une clé particulière mais pas 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 "commander" abandonnent si vous essayez.

La façon la plus propre de faire fonctionner cela, à mon avis, est quelques 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
s_ha_dum
la source