Können die Next / Prev Post-Links nach Menüreihenfolge oder nach einem Metaschlüssel sortiert werden?

32

Ich habe eine Reihe von Posts, die nach einem meta_key-Wert sortiert sind. Sie können bei Bedarf auch nach Menüreihenfolge arrangiert werden.

Die Links für den nächsten / vorherigen Beitrag (generiert von next_post_link, previous_post_linkoder posts_nav_linkalle navigieren nach Chronologie. Obwohl ich dieses Standardverhalten verstehe, verstehe ich nicht, wie ich es ändern kann dann sieht es ziemlich hart aus. Wird empfohlen, dies von Grund auf neu zu schreiben, um es zu ersetzen, oder gibt es eine bessere Lösung.

Jodi Warren
quelle
2
Hier ist das perfekte Plugin für dein Problem: wordpress.org/support/topic/… wordpress.org/extend/plugins/… Danke, Ambrosite! :)
Miguelb
1
Beachten Sie, dass die zweite Antwort das richtige Ergebnis zu liefern scheint.
Thomas

Antworten:

29

Interna verstehen

Die "Sortierreihenfolge" benachbarter (nächster / vorheriger) Beiträge ist eigentlich keine "Sortierreihenfolge". Es ist eine separate Abfrage auf jeder Anfrage / Seite, aber sie sortiert die Abfrage nach post_date- oder dem übergeordneten Element, wenn Sie einen hierarchischen Beitrag als aktuell angezeigtes Objekt haben.

Wenn Sie sich die Interna von ansehen next_post_link(), sehen Sie, dass es sich im Grunde um einen API-Wrapper handelt adjacent_post_link(). Die spätere Funktion ruft get_adjacent_post()intern mit dem $previousArgument / Flag auf, das gesetzt ist bool(true|false), um den nächsten oder vorherigen Beitragslink zu erfassen.

Was filtern?

Nachdem Sie sich näher damit befasst haben, werden Sie feststellen, dass der get_adjacent_post() Quelllink einige schöne Filter für seine Ausgabe enthält (auch als Abfrageergebnis bekannt): (Filtername / Argumente)

  • "get_{$adjacent}_post_join"

    $join
    // Only if `$in_same_cat`
    // or: ! empty( $excluded_categories` 
    // and then: 
    // " INNER JOIN $wpdb->term_relationships AS tr 
    //     ON p.ID = tr.object_id 
    // INNER JOIN $wpdb->term_taxonomy tt 
    //     ON tr.term_taxonomy_id = tt.term_taxonomy_id"; 
    // and if $in_same_cat then it APPENDS: 
    // " AND tt.taxonomy = 'category' 
    // AND tt.term_id IN (" . implode(',', $cat_array) . ")";
    $in_same_cat
    $excluded_categories
  • "get_{$adjacent}_post_where"

    $wpdb->prepare(
          // $op = $previous ? '<' : '>'; | $current_post_date
           "WHERE p.post_date $op %s "
          // $post->post_type
          ."AND p.post_type = %s "
          // $posts_in_ex_cats_sql = " AND tt.taxonomy = 'category' 
          // AND tt.term_id NOT IN (" . implode($excluded_categories, ',') . ')'; 
          // OR empty string if $in_same_cat || ! empty( $excluded_categories
          ."AND p.post_status = 'publish' $posts_in_ex_cats_sql "
        ",
        $current_post_date,
        $post->post_type
    )
    $in_same_cat
    $excluded_categories
  • "get_{$adjacent}_post_sort"

    "ORDER BY p.post_date $order LIMIT 1"`

Sie können also viel damit anfangen . Das beginnt mit dem Filtern der WHEREKlausel sowie der JOINed-Tabelle und der ORDER BYAnweisung.

Das Ergebnis wird für die aktuelle Anforderung im Arbeitsspeicher zwischengespeichert, sodass keine zusätzlichen Abfragen hinzugefügt werden, wenn Sie diese Funktion mehrmals auf einer einzelnen Seite aufrufen.

Automatische Abfrageerstellung

Wie @StephenHarris in den Kommentaren hervorhob , gibt es eine Kernfunktion, die sich beim Erstellen der SQL-Abfrage als nützlich erweisen könnte: get_meta_sql()- Beispiele im Codex . Grundsätzlich wird diese Funktion nur verwendet, um die Meta-SQL-Anweisung zu erstellen, in der sie verwendet WP_Querywird. Sie können sie jedoch auch in diesem Fall (oder in anderen Fällen) verwenden. Das Argument, das Sie hineinwerfen, ist ein Array, genau dasselbe, das zu einem hinzufügen würde WP_Query.

$meta_sql = get_meta_sql(
    $meta_query,
    'post',
    $wpdb->posts,
    'ID'
);

Der Rückgabewert ist ein Array:

$sql => (array) 'join' => array(),
        (array) 'where' => array()

So können Sie $sql['join']und $sql['where']in Ihrem Rückruf nutzen.

Abhängigkeiten im Hinterkopf behalten

In Ihrem Fall ist es am einfachsten, es in einem kleinen (mu) Plugin oder in der Datei functions.php Ihres Themes abzufangen und abhängig von der $adjacent = $previous ? 'previous' : 'next';Variablen und der $order = $previous ? 'DESC' : 'ASC';Variablen zu ändern :

Die tatsächlichen Filternamen

Die Filternamen lauten also:

  • get_previous_post_join, get_next_post_join
  • get_previous_post_where, get_next_post_where
  • get_previous_post_sort, get_next_post_sort

Als Plugin eingepackt

... und der Filter-Callback wäre (zum Beispiel) so ähnlich wie der folgende:

<?php
/** Plugin Name: (#73190) Alter adjacent post link sort order */
function wpse73190_adjacent_post_sort( $orderby )
{
    return "ORDER BY p.menu_order DESC LIMIT 1";
}
add_filter( 'get_previous_post_sort', 'wpse73190_adjacent_post_sort' );
add_filter( 'get_next_post_sort', 'wpse73190_adjacent_post_sort' );
Kaiser
quelle
2
+1. Nur zur Information (@magnakai), wenn Sie so etwas für Meta-Abfragen tun, lesen Sieget_meta_sql()
Stephen Harris
+1 an dich @StephenHarris! Habe ich noch nie gesehen. Kurze Frage: Wenn ich aus der Quelle lese, dass Sie ein vollqualifiziertes Abfrageobjekt übergeben müssen, wie würden Sie dies mit den oben genannten Filtern tun? Soweit ich sehe, werden nur Abfragezeichenfolgen übergeben, da die Abfrage nach den Filtern ausgeführt wird.
Kaiser
2
Nö, $meta_queryist nur das Array, an das Sie WP_Querydas meta_queryArgument übergeben würden: In diesem Beispiel: $meta_sql = get_meta_sql( $meta_query, 'post', $wpdb->posts, 'ID');- wird der Teil JOINund WHEREder Abfrage generiert, der hinzugefügt werden müsste.
Stephen Harris
@StephenHarris Perfekter Moment, um eine (meine) Antwort zu bearbeiten.
Kaiser
@StephenHarris, ich habe Probleme beim Anwenden der Ausgabe von get_meta_sql (). Können Sie dabei helfen, die Punkte zusammenzufügen?
Jodi Warren
21

Kaisers Antwort ist großartig und gründlich, es reicht jedoch nicht aus, die ORDER BY-Klausel zu ändern, es sei denn, Sie menu_orderstimmen mit Ihrer chronologischen Reihenfolge überein.

Ich kann das nicht gutschreiben , aber ich habe den folgenden Code in dieser Übersicht gefunden :

<?php
/**
 * Customize Adjacent Post Link Order
 */
function wpse73190_gist_adjacent_post_where($sql) {
  if ( !is_main_query() || !is_singular() )
    return $sql;

  $the_post = get_post( get_the_ID() );
  $patterns = array();
  $patterns[] = '/post_date/';
  $patterns[] = '/\'[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2}\'/';
  $replacements = array();
  $replacements[] = 'menu_order';
  $replacements[] = $the_post->menu_order;
  return preg_replace( $patterns, $replacements, $sql );
}
add_filter( 'get_next_post_where', 'wpse73190_gist_adjacent_post_where' );
add_filter( 'get_previous_post_where', 'wpse73190_gist_adjacent_post_where' );

function wpse73190_gist_adjacent_post_sort($sql) {
  if ( !is_main_query() || !is_singular() )
    return $sql;

  $pattern = '/post_date/';
  $replacement = 'menu_order';
  return preg_replace( $pattern, $replacement, $sql );
}
add_filter( 'get_next_post_sort', 'wpse73190_gist_adjacent_post_sort' );
add_filter( 'get_previous_post_sort', 'wpse73190_gist_adjacent_post_sort' );

Ich habe die Funktionsnamen für WP.SE geändert.

Wenn Sie nur die ORDER BY-Klausel ändern, sucht die Abfrage weiterhin nach Beiträgen, die größer oder kleiner als das aktuelle Beitragsdatum sind. Wenn Ihre Beiträge nicht chronologisch geordnet sind, erhalten Sie nicht den richtigen Beitrag.

Dadurch wird die where-Klausel geändert, um nach Posts zu suchen, bei denen menu_order größer oder kleiner als menu_order des aktuellen Posts ist. Außerdem wird die orderby-Klausel geändert.

Die orderby-Klausel sollte auch nicht hartcodiert sein, um DESC zu verwenden, da sie abhängig davon wechseln muss, ob Sie den nächsten oder den vorherigen Beitragslink erhalten.

jjeaton
quelle
3
Eine Anmerkung: Die WHEREKlausel sucht 'YYYY-mm-dd HH:mm:ss'. Wenn das nicht erfüllt ist, wird es nicht funktionieren. Da der Wert nicht von der Datenbank, sondern von der Anwendung festgelegt wird, müssen Sie beim Erstellen des regulären Ausdrucks zuerst nach diesem Format suchen.
Kaiser
5

Versuchte, ohne Erfolg einzuhaken. Könnte nur ein Problem meiner Konfiguration sein, aber für diejenigen, die den Hook nicht zum Laufen bringen können, ist hier die einfachste Lösung:

<?php
    $all_posts = new WP_Query(array(
        'orderby' => 'menu_order',
        'order' => 'ASC',
        'posts_per_page' => -1
    ));

    foreach($all_posts->posts as $key => $value) {
        if($value->ID == $post->ID){
            $nextID = $all_posts->posts[$key + 1]->ID;
            $prevID = $all_posts->posts[$key - 1]->ID;
            break;
        }
    }
?>
<?php if($prevID): ?>
    <span class="prev">
        <a href="<?= get_the_permalink($prevID) ?>" rel="prev"><?= get_the_title($prevID) ?></a>
    </span>
<?php endif; ?>
<?php if($nextID): ?>
    <span class="next">
        <a href="<?= get_the_permalink($nextID) ?>" rel="next"><?= get_the_title($nextID) ?></a>
    </span>
<?php endif; ?>
Szabolcs Páll
quelle
nach ein paar Stunden zu versuchen , zu erhalten get_previous_post_where, get_previous_post_joinund get_previous_post_sortzu spielen schön mit benutzerdefinierten Post - Typen und komplexe Anordnung , die Meta - Schlüssel enthält, gab ich auf und verwendet diese. Vielen Dank!
SquareCandy
Ebenso wollte ich hier nicht nur nach Menüreihenfolge bestellen, sondern auch nach Beiträgen mit einem bestimmten meta_key und meta_value suchen, daher war dies die beste Methode. Die einzige Änderung, die ich vorgenommen habe, war, es in eine Funktion zu packen.
MrCarrot
4
function wpse73190_gist_adjacent_post_sort( $sql ) {
    $pattern = '/post_date/';
    $replacement = 'menu_order';

    return preg_replace( $pattern, $replacement, $sql );
}

add_filter( 'get_next_post_sort', 'wpse73190_gist_adjacent_post_sort' );
add_filter( 'get_previous_post_sort', 'wpse73190_gist_adjacent_post_sort' );
Micheal Jess
quelle
1

Basierend auf der Antwort von @Szabolcs Páll habe ich diese Utility-Klasse mit Hilfsmethoden erstellt, um Einträge nach Menüreihenfolge und Einträge nach Menüreihenfolge abzurufen . Ich habe außerdem Bedingungen hinzugefügt, um zu überprüfen, ob der aktuelle Beitrag der erste oder der letzte ist, um den letzten bzw. den ersten Beitrag zu erhalten.

Beispielsweise:

// $currentPost is first by menu order
getPreviousPostByMenuOrder($postType, $$currentPost->ID)
// returns => last post by menu order

// $currentPost is last by menu order
getPreviousPostByMenuOrder($postType, $$currentPost->ID)
// returns => first post by menu order

Die volle Klasse:

class PostMenuOrderUtils {

    public static function getPostsByMenuOrder($postType){
        $args =[
            'post_type' => $postType,
            'orderby' => 'menu_order',
            'order' => 'ASC',
            'posts_per_page' => -1
        ];

        $posts = get_posts($args);

        return $posts;
    }

    public static function getNextPostByMenuOrder($postType, $postID){
        $posts = self::getPostsByMenuOrder($postType);

        $nextPost = null;

        foreach($posts as $key => $value) {
            if($value->ID == $postID){
                $nextPost = $posts[$key] !== end($posts) ? $posts[$key + 1] : $posts[0];

                break;
            }
        }

        return $nextPost;
    }

    public static function getPreviousPostByMenuOrder($postType, $postID){
        $posts = self::getPostsByMenuOrder($postType);


        $prevPost = null;

        foreach($posts as $key => $value) {
            if($value->ID == $postID){
                $prevPost = $key !== 0 ? $posts[$key - 1] : end($posts);
                break;
            }
        }

        return $prevPost;
    }

}
Eli Jayson
quelle
0

Ich finde dieses kleine Plugin sehr praktisch: http://wordpress.org/plugins/wp-query-powered-adjacent-post-link/

WP_Query Powered Adjacent Post Link ist ein Plugin für Entwickler. Es fügt die Funktion wpqpapl();zu WordPress hinzu, die Informationen zum vorherigen und nächsten Beitrag zum aktuellen zurückgeben kann. Es akzeptiert Argumente zur Verwendung in der WP_QueryKlasse.

any_h
quelle
0

Das hat bei mir funktioniert:

add_filter( 'get_previous_post_where', 'so16495117_mod_adjacent_bis' );
add_filter( 'get_next_post_where', 'so16495117_mod_adjacent_bis' );
function so16495117_mod_adjacent_bis( $where ) {
    global $wpdb;
    return $where . " AND p.ID NOT IN ( SELECT post_id FROM $wpdb->postmeta WHERE ($wpdb->postmeta.post_id = p.ID ) AND $wpdb->postmeta.meta_key = 'archive' AND $wpdb->postmeta.meta_value = 1 )";
}

Entnommen aus: https://stackoverflow.com/questions/16495117/how-to-skip-certain-links-on-adjacent-posts-in-wordpress

Philip
quelle
-1

Ich habe einen viel einfacheren Weg gefunden, um eine Meta-Key-basierte Post-Navigation zu erreichen, ohne dass die functions.php geändert werden muss.

Mein Beispiel: Sie haben eine products.php und möchten zwischen Produkten wechseln. Das vorherige Produkt ist das nächstbilligere, das nächste Produkt das nächstteuerere.

Hier kommt meine Lösung für single.php :

<div class="post_navigation">

<?php

// Prepare loop
$args = (
'post_type' => 'products',
'post_status' => 'publish',
'meta_key' => 'price',
'orderby' => 'meta_value_num',
'order' => 'ASC',
'posts_per_page' => -1
);
query_posts($args);

// Initialize array in which the IDs of ALL products posts will be stored
$posts = array();

// ... and now let's start the loop
while ( have_posts() ) : the_post();
$posts[] += $post->ID;
endwhile;

// Reset Query
wp_reset_query();

// Identify the position of the current product within the $posts-array 
$current = array_search(get_the_ID(), $posts);

// Identify ID of previous product
$prevID = $posts[$current-1];

// Identify ID of next product
$nextID = $posts[$current+1];

// Link "previous product"
if (!empty($prevID)) { ?>
<a href="/?p=<?php echo $prevID; ?>">previous product</a>
<?php }
// Link "next product"
if (!empty($nextID)) { ?>
<a href="/?p=<?php echo $nextID; ?>">next product</a>

<?php } ?>
Kent Miller
quelle
-10 für diese Antwort. Wie kann dies eine bessere Lösung sein, wenn Sie verwenden, query_postswenn der Codex angibt, dass es nicht verwendet werden sollte?
Pieter Goosen
Aber es funktioniert. Also die Alternative ist WP_Query oder was?
Kent Miller
Ja, WP_Querysollte wie in den vorherigen Antworten verwendet werden.
Pieter Goosen
1
@KentMiller, auf der Codex-Seite befindet sich ein informatives Diagramm , und möglicherweise ist diese Frage auch hilfreich. Es lohnt sich, sich mit diesen Konventionen vertraut zu machen.
Jodi Warren