Les liens Next / Prev Post peuvent-ils être commandés par ordre de menu ou par une touche méta?

32

J'ai une série de messages qui sont classés par valeur meta_key. Ils peuvent également être organisés par ordre de menu, si nécessaire.

Les liens de publication suivants / précédents (générés par next_post_link, previous_post_linkou posts_nav_linktous naviguent par chronologie. Bien que je comprenne ce comportement par défaut, je ne comprends pas comment le modifier. J'ai trouvé qu'il correspond à adjacent_post_link dans link-template.php, mais puis il commence à sembler assez codé en dur. Est-il recommandé de réécrire cela à partir de zéro pour le remplacer, ou existe-t-il une meilleure solution.

Jodi Warren
la source
2
Voici le plugin parfait pour votre problème: wordpress.org/support/topic/… wordpress.org/extend/plugins/… Merci Ambrosite! :)
miguelb
1
Notez que la deuxième réponse semble donner le résultat correct.
Thomas

Réponses:

29

Comprendre les internes

L'ordre de "tri" des articles adjacents (suivant / précédent) n'est pas vraiment un "ordre" de tri. Il s'agit d'une requête distincte sur chaque demande / page, mais elle trie la requête par le post_date- ou le parent du post si vous avez un post hiérarchique comme objet actuellement affiché.

Lorsque vous jetez un œil aux composants internes de next_post_link(), vous voyez que c'est essentiellement un wrapper API pour adjacent_post_link(). La dernière fonction appelle en get_adjacent_post()interne avec l' $previousargument / indicateur défini sur bool(true|false)pour saisir le lien de publication suivant ou précédent.

Que filtrer?

Après avoir creusé plus profondément, vous verrez que le get_adjacent_post() lien source a de bons filtres pour sa sortie (alias résultat de la requête): (Nom du filtre / Arguments)

  • "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"`

Vous pouvez donc en faire beaucoup . Cela commence par filtrer la WHEREclause, ainsi que la JOINtable ed et l' ORDER BYinstruction.

Le résultat est mis en cache en mémoire pour la demande actuelle, il n'ajoute donc pas de requêtes supplémentaires si vous appelez cette fonction plusieurs fois sur une seule page.

Création automatique de requêtes

Comme @StephenHarris l'a souligné dans les commentaires, il existe une fonction de base qui pourrait être utile lors de la construction de la requête SQL: get_meta_sql()- Exemples dans Codex . Fondamentalement, cette fonction est juste utilisée pour construire l'instruction meta SQL qui est utilisée dans WP_Query, mais vous pouvez également l'utiliser dans ce cas (ou dans d'autres). L'argument que vous y jetez est un tableau, exactement le même que celui qui ajouterait à a WP_Query.

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

La valeur de retour est un tableau:

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

Vous pouvez donc utiliser $sql['join']et $sql['where']dans votre rappel.

Dépendances à garder à l'esprit

Dans votre cas, le plus simple serait de l'intercepter dans un petit plugin (mu) ou dans votre fichier themes functions.php et de le modifier en fonction de la $adjacent = $previous ? 'previous' : 'next';variable et de la $order = $previous ? 'DESC' : 'ASC';variable:

Les noms de filtre réels

Les noms des filtres sont donc:

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

Enveloppé comme un plugin

... et le rappel du filtre serait (par exemple) quelque chose comme ceci:

<?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
la source
2
+1. Juste pour information, (@magnakai) si vous faites quelque chose comme ça pour les méta requêtes, consultezget_meta_sql()
Stephen Harris
+1 à vous @StephenHarris! Je n'ai jamais vu celui-ci auparavant. Question courte: Comme je l'ai lu dans la source que vous devez passer un objet de requête complet, comment feriez-vous avec les filtres mentionnés ci-dessus? Pour autant que je sache, seules les chaînes de requête sont passées, car la requête est exécutée après les filtres.
kaiser
2
Nope, $meta_queryest tout simplement le tableau que vous passeriez à WP_Queryla meta_queryargument: Dans cet exemple: $meta_sql = get_meta_sql( $meta_query, 'post', $wpdb->posts, 'ID');- cela génère la JOINet une WHEREpartie de la requête qui doit être ajouté.
Stephen Harris
@StephenHarris Moment idéal pour éditer une (ma) réponse.
Kaiser
@StephenHarris, j'ai du mal à appliquer la sortie de get_meta_sql () - pouvez-vous aider à joindre les points?
Jodi Warren
21

La réponse de Kaiser est impressionnante et approfondie, mais il ne suffit pas de modifier la clause ORDER BY à moins que votre menu_ordercorrespondance avec votre ordre chronologique.

Je ne peux pas m'en attribuer le mérite, mais j'ai trouvé le code suivant dans cet essentiel :

<?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' );

J'ai modifié les noms de fonction pour WP.SE.

Si vous modifiez uniquement la clause ORDER BY, la requête recherche toujours les publications supérieures ou inférieures à la date de publication actuelle. Si vos articles ne sont pas classés par ordre chronologique, vous n'obtiendrez pas le bon article.

Cela modifie la clause where pour rechercher les publications dont le menu_order est supérieur ou inférieur à la menu_order du message actuel, en plus de modifier la clause orderby.

La clause orderby ne doit pas non plus être codée en dur pour utiliser DESC car elle devra changer selon que vous obtenez le lien de publication suivant ou précédent.

jjeaton
la source
3
Une remarque: la WHEREclause cherche 'YYYY-mm-dd HH:mm:ss'. Si cela n'est pas respecté, cela ne fonctionnera pas. Comme la valeur n'est pas définie par la base de données, mais par l'application, vous devrez d'abord vérifier ce format lors de la création de l'expression régulière.
kaiser
5

J'ai essayé de me connecter sans succès. Cela pourrait être juste un problème de ma configuration, mais pour ceux qui ne peuvent pas faire fonctionner le crochet, voici la solution la plus simple:

<?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
la source
après quelques heures d'essayer d'obtenir get_previous_post_where, get_previous_post_joinet get_previous_post_sortde bien fonctionner avec les types de poste personnalisé et commande complexe qui comprend des touches méta, j'ai abandonné et utilisé cela. Merci!
squarecandy
Même chose ici, non seulement je voulais commander par ordre de menu, mais aussi rechercher des publications avec une méta-clé et une méta-valeur spécifiques, donc c'était la meilleure méthode. Le seul changement que j'ai fait a été de l'envelopper dans une fonction.
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
la source
1

Sur la base de la réponse de @Szabolcs Páll, j'ai créé cette classe d'utilité avec des méthodes d'assistance pour pouvoir obtenir des messages de type par ordre de menu et obtenir également le message suivant et précédent par ordre de menu. J'ai également ajouté des conditions pour vérifier si le message actuel est le premier ou le dernier message pour obtenir le dernier ou le premier message respectivement.

Par exemple:

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

La classe complète:

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
la source
0

Je trouve ce petit plugin vraiment pratique: http://wordpress.org/plugins/wp-query-powered-adjacent-post-link/

WP_Query Powered Adjacent Post Link est un plugin pour les développeurs. Il ajoute la fonction wpqpapl();à WordPress qui peut renvoyer des informations sur le post précédent et suivant au courant. Il accepte des arguments à utiliser dans la WP_Queryclasse.

any_h
la source
0

Cela a fonctionné pour moi:

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 )";
}

Tiré de: https://stackoverflow.com/questions/16495117/how-to-skip-certain-links-on-adjacent-posts-in-wordpress

Philippe
la source
-1

J'ai trouvé un moyen beaucoup plus facile de réaliser une navigation de post basée sur une méta-clé, sans avoir besoin de modifier functions.php.

Mon exemple: vous avez un products.php et vous souhaitez basculer entre les produits. Le produit précédent est le prochain moins cher, le prochain produit le prochain plus cher.

Voici ma solution pour 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
la source
-10 pour cette réponse. Comment cela peut-il être une meilleure solution si vous utilisez query_postslorsque le codex indique qu'il ne doit pas être utilisé.
Pieter Goosen
mais ça marche. donc l'alternative est WP_Query ou quoi?
Kent Miller,
Oui, WP_Querydoit être utilisé comme dans les réponses précédentes.
Pieter Goosen
1
@KentMiller, il y a un diagramme informatif sur la page du codex , et vous pouvez également trouver cette question utile. Il vaut la peine de vous familiariser avec ces conventions.
Jodi Warren