wake-up-neo.net

Nach Metawert sortieren, aber Beiträge einschließen, die keinen haben

Ich habe die integrierte Suche WP mithilfe des Filters pre_get_posts geändert, damit der Benutzer die Beiträge (einschließlich einer Reihe benutzerdefinierter Beitragstypen) nach verschiedenen Feldern sortieren kann.

Das Problem, das ich habe, ist, dass wenn ich WP anweise, nach einem Meta-Wert zu sortieren, alle Posts ausgeschlossen werden, für die dieser Meta-Wert nicht festgelegt ist. Dies führt dazu, dass sich die Anzahl der Ergebnisse ändert, wenn Sie die Sortierung von "Preis" auf "Datum" ändern, da für "Beiträge" nicht "Preis" festgelegt ist, sondern "Artikel".

Dies ist nicht das, was ich will, also würde ich gerne wissen, ob es eine Möglichkeit gibt, ALLE Posts einzuschließen - auch diejenigen, denen der Meta-Wert fehlt, nach dem ich sortiere - und denjenigen ohne den Wert als letzten zu setzen.

Ich weiß, wie man nach mehr als einem Feld sortiert, aber das hilft nicht.

Vielen Dank

Scheint, ich bin nicht der einzige mit dieser Frage: Möglichkeit, Beiträge mit und ohne bestimmten meta_key in args for wp_query aufzunehmen? aber da gibt es keine lösung.

Update

Ich habe die Antwort ausprobiert, bin mir aber nicht sicher, ob ich sie richtig verstanden habe:

<?php
function my_stuff ($qry) {
    $qry->set('meta_query', array(array(
        'key' => 'item_price', 
        'value' => '', 
        'compare' => 'NOT EXISTS'
    )));

    $qry->set('orderby', 'meta_value date'); # Sorting works with meta_value as well as meta_value_num - I've tried both
    $qry->set('order', 'ASC DESC');
    $qry->set('meta_key', 'item_price');
}

Der Meta-Wert ist eine Zahl (er wird verwendet, um einen Preis zu speichern, wie der Name vermuten lässt.)

Update 2

Ich habe die Bestellung auskommentiert und alles, was ich jetzt habe, ist Folgendes:

<?php
$qry->set('meta_query', array(array(
    'key' => 'item_price', 
    'value' => '', 
    'compare' => 'NOT EXISTS'
)));

Mit diesem Code scheint die Abfrage alle Posts zurückzugeben, die nicht den Schlüssel item_price haben, und keinen der Posts, die diesen Schlüssel haben. I.E. Das Problem ist jetzt umgekehrt.

Wenn ich auch den Bestellcode hinzufüge, erhalte ich 0 Ergebnisse.

Edit: ... drei Jahre später ...: P Ich hatte dieses Problem erneut. Ich habe alle Antworten ausprobiert und keine funktioniert. Ich bin mir nicht sicher, warum manche Leute glauben, dass sie funktionieren, aber sie arbeiten zumindest nicht für mich.

Die Lösung, die ich gefunden habe, ist die Verwendung des save_post-Filters - stellen Sie sicher, dass alle Beiträge über das benutzerdefinierte Feld verfügen, nach dem ich sortieren möchte. Es ist ein bisschen ärgerlich, dass ich es tun muss, aber solange Sie es früh tun, werden Sie wahrscheinlich keine Probleme haben.

In diesem Fall habe ich einen "View Counter" für Posts erstellt und wollte, dass Benutzer in der Lage sind, die am häufigsten gelesenen Posts zu sortieren. Wiederum verschwanden Beiträge, die noch nie gesehen wurden (ich denke, das ist ziemlich unwahrscheinlich - aber immer noch), wenn nach Ansichtsanzahl sortiert wurde. Ich habe diesen Code hinzugefügt, um sicherzustellen, dass alle Beiträge eine Anzahl von Ansichten haben:

add_action('save_post', function ($postId) {
    add_post_meta($postId, '_sleek_view_count', 0, true);
});
30
powerbuoy

Hierfür gibt es zwei mögliche Lösungen:

1. Alle Beiträge haben Meta

Die beste Lösung, die ich hier gefunden habe, besteht darin, den restlichen Artikeln/Produkten einen Artikelpreis von 0 zuzuweisen. Sie können dies manuell tun oder alle Artikel durchlaufen. Wenn der Preis leer ist, aktualisieren Sie ihn.

Um dies in Zukunft handhabbar zu machen, können Sie sich in save_post einbinden und ihnen beim ersten Hinzufügen einen Wert zuweisen (nur wenn dieser leer ist).

2. Mehrere Abfragen

Sie können die erste Abfrage währenddessen ausführen und die IDs der zurückgegebenen Posts speichern. Sie könnten dann eine weitere Abfrage für alle Posts und das Bestelldatum ausführen, mit Ausnahme der IDs, die von der ersten Abfrage zurückgegeben werden.

Sie können dann die beiden Ergebnisse separat bestellen und Sie erhalten die gewünschten Ergebnisse.

5
Steven Jones

Diese Methode gibt alle Posts zurück, einschließlich der Posts mit und ohne den angeforderten meta_key. Bei der Bestellung werden jedoch merkwürdige Dinge ausgeführt.

add_action('pre_get_posts', 'my_stuff');
function my_stuff ($qry) {
    $qry->set(
        'meta_query',
        array(
            'relation' => 'OR', # Matches to this meta_query should be added to those matching the 'meta_key' query
            array(
                'key' => 'item_price', 
                'value' => 'bug #23268', 
                'compare' => 'NOT EXISTS'
            )
        )
    );

    $qry->set('orderby', 'meta_value date'); # Sorting works with meta_value as well as meta_value_num - I've tried both
    $qry->set('order', 'ASC DESC');
    $qry->set('meta_key', 'item_price');
}

Ich fand dies, indem ich mit all den verschiedenen Antworten auf diese Frage herumspielte und das generierte SQL durch Ausprobieren analysierte. Es scheint, dass die Einstellung array('meta_query' => array('relation' => 'OR')) einen geeigneten LEFT JOIN anstelle von INNER JOIN ausgibt, der erforderlich ist, um Beiträge einzuschließen, bei denen die Metadaten fehlen. Durch die Angabe von NOT EXISTS wird verhindert, dass in der WHERE-Klausel Posts ohne Metafeld herausgefiltert werden. Für diesen speziellen WP_Query lautet die generierte SQL (Einrückung/Zeilenumbruch hinzugefügt):

SELECT SQL_CALC_FOUND_ROWS
    wp_posts.ID
    FROM wp_posts
    INNER JOIN wp_term_relationships ON (wp_posts.ID = wp_term_relationships.object_id)
    INNER JOIN wp_postmeta ON wp_posts.ID = wp_postmeta.post_id
    LEFT JOIN wp_postmeta AS mt1 ON (wp_posts.ID = mt1.post_id AND mt1.meta_key = 'item_price')
    WHERE 1=1
    AND ( wp_term_relationships.term_taxonomy_id IN (2) )
    AND wp_posts.post_type = 'post'
    AND (wp_posts.post_status = 'publish'
        OR wp_posts.post_status = 'private')
    AND (wp_postmeta.meta_key = 'item_price'
        -- Oh look, here we give SQL permission to choose a random
        -- row from wp_postmeta when this particular post is missing
        -- 'item_price':
        OR  mt1.post_id IS NULL )
    GROUP BY wp_posts.ID
    ORDER BY wp_postmeta.meta_value,wp_posts.post_date DESC
    LIMIT 0, 10

Das Ergebnis ist eine Auflistung aller Posts mit dem Meta-Wert item_price und den fehlenden item_price. Alle Beiträge mit item_price werden relativ zueinander korrekt sortiert, aber fehlende Beiträge item_price verwenden einen zufälligen othermeta value (sagen wir, _edit_last, der in meiner Datenbank ziemlich häufig 1 zu sein scheint oder einige andere interne WordPress-Metadaten (die völlig willkürlich sind) für ihren wp_postmeta.meta_value in der ORDER BY-Klausel. Obwohl diese Methode nahe beieinander liegt und für bestimmte Daten zu funktionieren scheint, ist sie fehlerhaft. Ich kann also nur sagen, ob Ihr item_price Es kommt vor, dass Werte nicht mit den zufälligen Metafeldern in Konflikt stehen, die MySQL für die fehlenden Beiträge item_price auswählt. Wenn Sie lediglich die Garantie benötigen, dass Ihre Beiträge mit item_price ohne Berücksichtigung der Reihenfolge von richtig zueinander angeordnet sind, funktioniert dies möglicherweise Bei anderen Posts ist es vielleicht in Ordnung, aber ich denke, dies ist nur ein Mangel in WordPress. Bitte korrigieren Sie mich, ich hoffe, ich liege falsch und es gibt einen Weg, dies zu beheben ;-).

Es scheint, dass MySQL für den INNER JOIN wp_postmeta eine zufällige Zeile aus mehreren postmeta-Zeilen auswählt, die dem Beitrag zugeordnet sind, wenn der meta_key im angegebenen Beitrag fehlt. Aus SQL-Sicht müssen wir herausfinden, wie WordPress angewiesen wird, ORDER BY mt1.meta_value auszugeben. Diese Spalte ist ordnungsgemäß NULL, wenn unser angeforderter meta_key fehlt, im Gegensatz zu wp_postmeta.meta_value. Wenn wir das tun könnten, würde SQL diese NULLfehlenden Einträge) vor jedem anderen Wert sortieren und uns eine genau definierte Reihenfolge geben: Zuerst kommen alle Beiträge, denen das jeweilige Postmetafeld fehlt, und dann die Beiträge mit dem Feld. Aber das ist das ganze Problem: 'orderby' => 'meta_value' kann sich nur auf 'meta_key' => 'item_price' beziehen (undder unausgeglichene wp_postmeta ist immer ein INNER JOIN anstatt jemals ein LEFT JOIN, dh wp_postmeta.meta_value und wp_postmeta.meta_key können niemalssein NULL).

Ich muss also sagen, dass dies mit dem in WordPress integrierten WP_Query nicht möglich ist, wie es jetzt dokumentiert ist (in WordPress-3.9.1). Mühe. Wenn Sie dies also tatsächlich benötigen, um richtig zu funktionieren, müssen Sie wahrscheinlich sich an einer anderen Stelle in WordPress einbinden und die generierte SQL direkt ändern .

8
binki

Easy Peasy, gerade 2018 getestet, wird derzeit in der Produktion verwendet.

$query->set( 'meta_query', array(
    'relation' => 'OR',
    array(
        'key' => 'custom_meta_key', 
        'compare' => 'EXISTS'
    ),
    array(
        'key' => 'custom_meta_key', 
        'compare' => 'NOT EXISTS'
    )
) );
$query->set( 'orderby', 'meta_value title' ); 

Hiermit wird nach allen Elementen mit und ohne Metaschlüssel gesucht, für die kein Wert angegeben wurde. Die Meta-Abfrage liefert zuverlässig den Schlüssel für die Bestellung. Es wurde getestet. Ich bin mir jedoch nicht sicher, wie es funktionieren wird, wenn die Meta-Abfrage mehrere Schlüssel verwendet.

Praktisches Beispiel

/**
 * Modifies query before retrieving posts. Sets the 
 * `meta_query` and `orderby` param when no `orderby` 
 * param is set, (default ordering).
 * 
 * @param   WP_Query  $query  The full `WP_Query` object.
 * @return  void
 */
function example_post_ordering( $query ) {

    // if not in wp-admin, 
    // and the query is the main query, 
    // and the query is not a singular query, 
    // and the query does not have an orderby param set...
    // Note: check for post types, etc. here as desired.
    if ( ! is_admin() 
    && $query->is_main_query() 
    && ! $query->is_singular() 
    && empty( $query->get( 'orderby' ) ) ) {

        // Setting just `meta_key` is not sufficient, as this 
        // will ignore posts that do not yet, or never will have 
        // a value for the specified key. This meta query will 
        // register the `meta_key` for ordering, but will not 
        // ignore those posts without a value for this key.
        $query->set( 'meta_query', array(
            'relation' => 'OR',
            array(
                'key' => 'custom_meta_key', 
                'compare' => 'EXISTS'
            ),
            array(
                'key' => 'custom_meta_key', 
                'compare' => 'NOT EXISTS'
            )
        ) );

        // Order by the meta value, then by the title if multiple 
        // posts share the same value for the provided meta key.
        // Use `meta_value_num` if the meta values are numeric.
        $query->set( 'orderby', 'meta_value title' );
    }

}

add_action( 'pre_get_posts', 'example_post_ordering', 10 );

Dadurch werden Beiträge standardmäßig nach custom_meta_key sortiert und Beiträge ohne einen Wert für diesen Schlüssel nicht ignoriert.

4
noahmason

Ich denke ich habe eine Lösung.

Sie können zwei meta_keys verwenden, einen, den alle Beiträge (like "_thumbnail_id") haben, und den meta_key, den Sie als Filter verwenden möchten.

Also deine Argumente:

$qry->set(
    'meta_query',
    array(
        'relation' => 'OR',
        array(
            'key' => 'item_price', 
            'value' => '', 
            'compare' => 'EXISTS'
        ),
        array(
            'key' => 'item_price', 
            'value' => '', 
            'compare' => 'EXISTS'
        )
    )
);

$qry->set('orderby', 'meta_value date'); # Sorting works with meta_value as well as meta_value_num - I've tried both
$qry->set('order', 'ASC DESC');
$qry->set('meta_key', 'item_price');
2
Rafael M31

Bei Bedarf können Sie jedes Mal, wenn ein Beitrag gespeichert oder aktualisiert wird, einen Standard-Metawert hinzufügen, wenn der Metawert nicht vorhanden ist.

function addDefaultMetaValue($post_id) {
    add_post_meta($post_id, 'item_price', 0, true);
}
add_action('save_post', 'addDefaultMetaValue');

Wenn Sie einen benutzerdefinierten Beitragstyp verwenden, ersetzen Sie add_action('save_post', 'addDefaultMetaValue'); durch add_action('save_post_{post_type}', 'addDefaultMetaValue');, z. add_action('save_post_product', 'addDefaultMetaValue');

1
rjpedrosa

Ich hatte das Problem alleine für numerische Metawerte und wies darauf hin, dass es auch auf die Reihenfolge der Abfrage ankommt. Für mich muss die NOT EXISTS Abfrage die erste sein.

Beispiel:

$query->set( 'orderby', 'meta_value_num' );
$query->set( 'meta_query', [
    'relation' => 'OR',
    [ 'key' => 'your_meta_name', 'compare' => 'NOT EXISTS' ],
    [
        'key' => 'your_meta_name',
        'compare' => 'EXISTS',
    ],
] );

Um die richtige Richtung für numerische Werte zu erhalten, ist es auch wichtig, den allgemeinen ’orderby’ auf ’meta_value_num’ zu setzen. Andernfalls erhalten Sie ungewöhnliche Ergebnisse für numerische Werte, z. G.:

1, 2, 20, 21, 3, 4, 5…

Anstatt:

1, 2, 3, 4, 5… 20, 21

0
KittMedia

Dafür gibt es einen möglichen Wert für orderby von meta_value.

$query = new WP_Query( array ( 
    'meta_key'   => 'your_keys_name',
    'orderby'    => 'meta_value',
    'order'      => 'DESC',
    'meta_query' => array( array(
         'key'     => 'your_meta_key',
         'value'   => '',
         'compare' => 'NOT EXISTS',
         // 'type'    => 'CHAR',
    ) )
) );

Wenn Sie numerische Werte haben, verwenden Sie stattdessen einfach meta_value_num.

Haftungsausschluss: Dies ist nicht getestet, aber es sollte funktionieren. Der Punkt ist, dass Sie Ihre Werte für meta_key und key angeben müssen. Ansonsten können Sie nicht mit nicht vorhandenen Werten vergleichen, wodurch es möglich sein sollte, beide Arten von Posts abzufragen. Es ist eine Art Hack-ish, aber solange es funktioniert ...

0
kaiser

Ich habe das mit ein bisschen Hack (IMHO) umgangen, aber es hat in meinem Fall die Arbeit für mich erledigt.

Sie können die Filter posts_join_paged und posts_orderby einbinden, um die Verknüpfungs- und Ordnungszeichenfolgen zu aktualisieren. Auf diese Weise können Sie nach Belieben bestellen, solange Sie sich zuerst anmelden und nicht nach WP_Query, vorausgesetzt, das Feld muss für diesen bestimmten Beitrag vorhanden sein. Sie können dann den meta_key, die orderby und `order aus Ihren WP_Query-Argumenten entfernen.

Unten ist ein Beispiel. Am Anfang jeder Funktion musste ich für bestimmte Fälle aussteigen, da dies zu allem hinzugefügt wird, was WP_Query verwendet. Möglicherweise müssen Sie dies an Ihre speziellen Bedürfnisse anpassen.

Die Dokumentation zu diesen beiden Filtern fehlt leider so ... viel Glück! :)

add_filter('posts_join_paged', 'edit_join', 999, 2);
add_filter('posts_orderby', 'edit_orderby', 999, 2);

/**
 * Edit join
 *
 * @param string $join_paged_statement
 * @param WP_Query $wp_query
 * @return string
 */
function edit_join($join_paged_statement, $wp_query)
{
    global $wpdb;
    if (
        !isset($wp_query->query)
        || $wp_query->is_page
        || $wp_query->is_admin
        || (isset($wp_query->query['post_type']) && $wp_query->query['post_type'] != 'my_custom_post_type')
    ) {
        return $join_paged_statement;
    }

    $join_to_add = "
        LEFT JOIN {$wpdb->prefix}postmeta AS my_custom_meta_key
            ON ({$wpdb->prefix}posts.ID = my_custom_meta_key.post_id
                AND my_custom_meta_key.meta_key = 'my_custom_meta_key')
    ";

    // Only add if it's not already in there
    if (strpos($join_paged_statement, $join_to_add) === false) {
        $join_paged_statement = $join_paged_statement . $join_to_add;
    }

    return $join_paged_statement;
}

/** 
 * Edit orderby
 *
 * @param string $orderby_statement
 * @param WP_Query $wp_query
 * @return string
 */
function edit_orderby($orderby_statement, $wp_query)
{
    if (
        !isset($wp_query->query)
        || $wp_query->is_page
        || $wp_query->is_admin
        || (isset($wp_query->query['post_type']) && $wp_query->query['post_type'] != 'my_custom_post_type')
    ) {
        return $orderby_statement;
    }

    $orderby_statement = "my_custom_meta_key.meta_value DESC";

    return $orderby_statement;
}
0
Scruffy Paws

Ich bin auch auf ein ähnliches Problem gestoßen und die folgende Lösung hat mir geholfen:

$args = array(
'post_type' => 'kosh_products',
'posts_per_page' => -1,
'meta_query' => array(
    'relation' => 'OR',
    'category_sort_order' => array(
        'key' => '_sort_order',
        'compare' => 'EXISTS'
    ),
    'category_sort_order_not_exists' => array(
        'key' => '_sort_order',
        'compare' => 'NOT EXISTS'
    ), 
),
'orderby' => array( 
    'category_sort_order' => 'ASC',
    'date' => 'ASC'
));
$query = new WP_Query( $args );

Ich habe eine Beschreibung in WordPress Codex mit dem Titel " 'orderby' mit mehreren 'meta_key's " gefunden: https://codex.wordpress.org/Class_Reference/WP_Query#Order_.26_Orderby_Parameters enter image description here 

0
Ilya Kogan

Ich denke, @kaiser hat versucht, der Abfrage mitzuteilen, dass alle Posts mit diesem Metaschlüssel zurückgegeben werden sollen, indem eine Art Dummy angewendet wird where condition to not Filter für einen dieser Posts. Wenn Sie also wissen, dass alle die Werte, die Ihre benutzerdefinierten Felder annehmen können, x, y, z sind, können Sie "WHERE meta_key IN (x, y, z) " sagen, aber die Idee ist, dass Sie dies vermeiden können das Problem alle zusammen mit! = ('') :

$query = new WP_Query( array ( 
    'orderby'    => 'meta_value_num',
    'order'      => 'DESC',
    'meta_query' => array( array(
         'key'     => 'item_price',
         'value'   => '',
         'compare' => '!=',
    ) )
) );

Auch nicht getestet, aber es scheint einen Versuch wert zu sein :-).

0
Jon

Das Problem, das jeder hier hat, hat mit der Reihenfolge der Meta-Abfragen zu tun. Um richtig zu sortieren, müssen Sie die Abfrage "NOT EXISTS" vor die Abfrage "EXISTS" setzen.

Der Grund dafür ist, dass WordPress den meta_value der letzten "LEFT JOIN" -Anweisung in der "ORDER BY" -Klausel verwendet.

Zum Beispiel:

$pageQuery = new WP_Query([
    'meta_query' => [
        'relation' => 'OR',
        ['key' => 'item_price', 'compare' => 'NOT EXISTS'], // this comes first!
        ['key' => 'item_price', 'compare' => 'EXISTS'],
    ],
    'order' => 'DESC',
    'orderby' => 'meta_value_num',
    'post_status' => 'publish',
    'post_type' => 'page',
    'posts_per_page' => 10,
]);
0
Paul