it-swarm.com.de

Orderby meta_value gibt nur Posts zurück, bei denen meta_key vorhanden ist

Ich habe folgende wp_query:

$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 Ergebnisse, da es nur 10 news Beiträge mit einem meta_key = custom_author_name gibt. Es gibt jedoch Hunderte von news Posts, die keine post_meta-Zeile mit diesem bestimmten meta_key haben. Bitte beachten Sie, dass es keine meta_query gibt. Es ist kein meta_value zugewiesen, da ich nur versuche, die Beiträge nach meta_key zu sortieren und nicht nach meta_value zu filtern.

Sollte nicht bestellt werden, indem alle Beiträge ausgewählt werden? und einfach bestellen?

Wenn ja, warum wird das Ergebnis gefiltert? Wenn der meta_key nicht gefunden wird, warum nicht einfach eine leere Zeichenfolge oder eine Übereinstimmung mit allen verwenden?

Wenn nein, warum nicht?

Wenn ich zu jedem Nachrichtenbeitrag einen meta_key eingebe (auch wenn es sich um eine leere Zeichenfolge handelt), erhalte ich das erwartete Ergebnis. Aber das scheint eine Menge Tabellenzeilen zu sein, die nicht vorhanden sein müssen.

10
gdaniel

Wie in @ ambroseyas Antwort angegeben, soll es so funktionieren. Sobald Sie eine Meta-Abfrage deklariert haben, werden nur Posts mit diesem deklarierten Metaschlüssel abgefragt, auch wenn Sie nicht nach einem bestimmten Wert suchen. Wenn Sie alle Posts einschließen möchten, sortieren Sie sie nach dem Metaschlüssel. Verwenden Sie dazu den folgenden Code:

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

Dazu wird eine erweiterte Metaabfrage verwendet, die nach Posts sucht, für die dieser Metaschlüssel deklariert wurde und für die dieser nicht deklariert wurde. Da die mit EXISTS die erste ist, wird beim Sortieren nach meta_value die erste Abfrage verwendet.

8
Manny Fleurmond

Ich habe versucht, @Manny Fleurmonds Antwort anzuwenden und wie @Jake konnte ich es nicht zum Laufen bringen, obwohl ich den Tippfehler korrigiert hatte, dass 'orderby' => 'meta_key''orderby' => 'meta_value' sein sollte. (Und der Vollständigkeit halber sollte es 'posts_per_page' nicht 'post_per_page' sein, aber das hat keinen Einfluss auf das betrachtete Problem.)

Wenn Sie sich die SQL-Abfrage ansehen, die tatsächlich von @Manny Fleurmonds Antwort generiert wurde (nachdem Sie die Tippfehler korrigiert haben), erhalten Sie Folgendes:

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

Dies zeigt, wie WP die Abfragevariablen analysiert: Es wird eine Tabelle für jede meta_query-Klausel erstellt und dann herausgefunden, wie sie verknüpft werden und nach welcher Reihenfolge sie sortiert werden sollen. Die Bestellung würde gut funktionieren, wenn Sie nur eine einzige Klausel mit 'compare' => 'EXISTS' verwenden, aber wenn Sie die zweite 'compare' => 'NOT EXISTS'-Klausel mit OR (wie wir müssen) verbinden, wird die Bestellung durcheinander gebracht. Das Ergebnis ist, dass LEFT JOIN verwendet wird, um sowohl die erste Klausel/Tabelle als auch die zweite Klausel/Tabelle zu verknüpfen - und die Art und Weise, wie WP alles zusammenfügt, bedeutet, dass die mit 'compare' => 'EXISTS' erstellte Tabelle tatsächlich mit meta_values ​​aus gefüllt wird JEDES benutzerdefinierte Feld, nicht nur das 'custom_author_name'-Feld, an dem wir interessiert sind. Ich denke, dass das Ordnen nach dieser Klausel/Tabelle nur dann die gewünschten Ergebnisse liefert, wenn der bestimmte post_type von 'news' nur ein einzelnes benutzerdefiniertes Feld enthält.

Die Lösung, die für meine Situation funktionierte, war, nach der anderen Klausel/Tabelle zu bestellen - der NOT EXISTS-Klausel. Ich weiß, dass dies scheinbar nicht intuitiv ist, aber aufgrund der Art und Weise, wie WP die Abfragevariablen analysiert, ist es diese Tabelle, in der meta_value nur durch das benutzerdefinierte Feld aufgefüllt wird, nach dem wir suchen.

(Die einzige Möglichkeit, dies herauszufinden, bestand darin, die Entsprechung dieser Abfrage für meinen Fall auszuführen:

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

Ich habe nur die angezeigten Spalten geändert und die GROUP BY-Klausel entfernt. Dies zeigte mir dann, was los war - dass die Spalte postmeta.meta_value Werte von allen meta_keys einliess, während die Spalte mt1.meta_value nur meta_values ​​aus dem benutzerdefinierten Feld news einliess.)

Die Lösung

Wie @Manny Fleurmond sagt, ist es die erste Klausel, die für das orderby verwendet wird. Die Antwort ist also, die Klauseln einfach zu vertauschen.

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

Alternativ können Sie die assoziativen Arrays der Klauseln erstellen und nach dem entsprechenden Schlüssel wie folgt sortieren:

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

So funktioniert das eigentlich.

Wenn Sie dies tun möchten, ohne Tabellenzeilen hinzuzufügen, müssen Sie zwei Abfragen durchführen. Einer mit dem meta_key, der die begrenzten Ergebnisse liefert, und der andere, der die gesamte Liste abruft. Verwenden Sie dann PHP, um die beiden Abfrageergebnisse zu vergleichen (möglicherweise entfernen Sie die meta_key-Ergebnisse aus der anderen Abfrage, um Duplikate zu entfernen, oder was auch immer in Ihrer Einstellung sinnvoll ist).

1
ambroseya

Leider funktioniert WP_Query nicht so. Sobald Sie diese "Meta" -Komponente hinzufügen, haben Sie eine Art Filter erstellt. Geben Sie $query->request ein und Sie werden sehen, was ich meine.

Zweitens unterstützt WP_Query das Bestellen nach einem Meta key überhaupt nicht. Sie können nach einem Meta Wert für einen bestimmten Schlüssel, aber nicht nach dem Schlüssel selbst bestellen. Speichern Sie die Abfrage erneut, um zu sehen, was ich meine. Sie werden feststellen, dass die Komponenten "order" ausfallen, wenn Sie es versuchen.

Der sauberste Weg, dies zum Laufen zu bringen, sind meiner Meinung nach ein paar kurze Filter:

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