Orderby meta_value gibt nur Beiträge zurück, für die meta_key vorhanden ist


10

Ich habe die 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 newsBeiträge mit a gibt meta_key = custom_author_name. Es gibt jedoch Hunderte von newsPosts, die keine post_meta-Zeile mit diesem bestimmten meta_key haben. Bitte beachten Sie, dass keine meta_query beteiligt ist. Es wird kein meta_value zugewiesen, da ich nur versuche, die Beiträge nach meta_key zu sortieren und nicht nach meta_value zu filtern.

Sollte orderby nicht alle Beiträge auswählen? 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 nicht, warum nicht?

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

Antworten:


10

Wie in der Antwort von @ ambroseya angegeben, soll es so funktionieren. Sobald Sie eine Metaabfrage deklarieren, werden nur Beiträge mit diesem deklarierten Metaschlüssel abgefragt, auch wenn Sie nicht nach einem bestimmten Wert suchen. Wenn Sie alle Beiträge einschließen möchten, sortieren Sie sie nach dem Metaschlüssel. Verwenden Sie den folgenden Code:

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

Dazu wird eine erweiterte Metaabfrage verwendet, die nach Posts sucht, bei denen dieser Metaschlüssel deklariert ist und nicht. Da der mit EXISTSzuerst ist, wird beim Sortieren nach meta_valuedie erste Abfrage verwendet.


3
Dies hat die Reihenfolge für mich überhaupt nicht geändert, als ich sie verwendet habe 'orderby' => 'meta_value', hat sie die Reihenfolge geändert, aber es hatte nichts mit dem tatsächlichen Metafeld zu tun.
Jake

3

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

Wenn Sie sich die SQL-Abfrage ansehen, die tatsächlich durch die Antwort von @Manny Fleurmond 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 erstellt eine Tabelle für jede meta_query-Klausel und findet dann heraus, wie sie verbunden werden sollen und nach was sortiert werden soll. Die Bestellung würde gut funktionieren, wenn Sie nur eine einzige Klausel mit verwenden würden 'compare' => 'EXISTS', aber wenn Sie die zweite 'compare' => 'NOT EXISTS'Klausel mit OR verbinden (wie wir müssen), 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 verbinden. Die Art und Weise, wie WP alles zusammenfügt, bedeutet, dass die mit erstellte Tabelle 'compare' => 'EXISTS'tatsächlich mit meta_values ​​aus JEDEM benutzerdefinierten Feld gefüllt wird, nicht nur mit 'custom_author_name'Feld, an dem wir interessiert sind. Daher denke ich, dass die Bestellung nach dieser Klausel / Tabelle nur dann die gewünschten Ergebnisse liefert, wenn der bestimmte post_type von 'news' nur ein einziges benutzerdefiniertes Feld enthält.

Die Lösung, die für meine Situation funktionierte, bestand darin, nach der anderen Klausel / Tabelle zu bestellen - der NOT EXISTS-Klausel. Ich weiß, dass dies scheinbar kontraintuitiv ist, aber aufgrund der Art und Weise, wie WP die Abfragevariablen analysiert, wird diese Tabelle meta_valuenur mit dem benutzerdefinierten Feld gefüllt, nach dem wir suchen.

(Der einzige Weg, dies herauszufinden, bestand darin, das Äquivalent 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 lediglich 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 aus allen meta_keys abrief, während die Spalte mt1.meta_value nur meta_values ​​aus dem benutzerdefinierten Nachrichtenfeld abrief.)

Die Lösung

Wie @Manny Fleurmond sagt, ist es die erste Klausel, die für die Bestellung verwendet wird. Die Antwort besteht also darin, die Klauseln zu vertauschen und Folgendes zu geben:

$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 Klauseln zu assoziativen Arrays machen und nach dem entsprechenden Schlüssel sortieren, wie folgt:

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

Es ist erwähnenswert, dass, wenn der Metaschlüssel custom_author_namegesetzt und dann nicht gesetzt ist, dies darauf meta_keyreagiert EXISTSund der Effekt ist, dass diese neben Posts mit einem sprudeln custom_author_name. In meinem Fall habe ich ein Kontrollkästchen, das ich "value" => "1"anstelle von verwende EXISTS, aber Zeichenfolgen benötigen einen anderen Ansatz.
DJB

1

So funktioniert es 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 hat, und der andere, der die gesamte Liste erhält; 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).


0

Leider WP_Queryfunktioniert das nicht so . Sobald Sie diese "Meta" -Komponente hinzufügen, haben Sie eine Art Filter erstellt. Dump $query->requestund du wirst sehen, was ich meine.

Zweitens wird WP_Querydie Bestellung nach einem Meta- Schlüssel überhaupt nicht unterstützt . Sie können durch einen Meta bestellen Wert für einen bestimmten Schlüssel , aber nicht durch den Schlüssel selbst. Speichern Sie die Abfrage erneut, um zu sehen, was ich meine. Sie werden feststellen, dass die "Bestell" -Komponenten 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
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.