web-dev-qa-db-fra.com

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

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

Les liens next/prev post (générés par next_post_link, previous_post_link ou posts_nav_link parcourent tous par chronologie. Bien que je comprenne ce comportement par défaut, je ne comprends pas comment le changer. J'ai constaté qu'il mappait sur adjacent_post_link dans link-template. php, mais alors cela commence à sembler assez codé en dur. Est-il recommandé de réécrire ceci à partir de zéro pour le remplacer, ou existe-t-il une meilleure solution?.

31
Jodi Warren

Comprendre les internes

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

Lorsque vous examinez les éléments internes de next_post_link() , vous constatez qu’il s’agit essentiellement d’un wrapper d’API pour adjacent_post_link() . La dernière fonction appelle get_adjacent_post() en interne avec l'argument/le code $previous défini sur bool(true|false) pour saisir le lien suivant ou précédent.

Que filtrer?

Après avoir approfondi, vous verrez que get_adjacent_post() Lien source  a quelques filtres de Nice pour sa sortie (résultat de la requête.k.a.): (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"`
    

Donc, vous pouvez faire beaucoup avec elle. Cela commence par le filtrage de la clause WHERE, ainsi que de la table JOINed et de l'instruction ORDER BY.

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

Création automatique de requêtes

Comme le souligne @StephenHarris dans les commentaires, il existe une fonction essentielle qui pourrait s'avérer utile lors de la construction de la requête SQL: get_meta_sql() - Exemples dans Codex . Fondamentalement, cette fonction est simplement utilisée pour construire la déclaration méta SQL utilisée dans WP_Query, mais vous pouvez également l'utiliser dans ce cas (ou dans d'autres). L'argument que vous lancez est un tableau, exactement le même que celui qui serait ajouté à un 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 functions.php de themes et de le modifier en fonction de la variable $adjacent = $previous ? 'previous' : 'next'; et de la variable $order = $previous ? 'DESC' : 'ASC';:

Les noms de filtres actuels

Donc, les noms de filtres sont:

  • 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) comme suit:

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

La réponse de Kaiser est géniale et complète. Cependant, changer la clause ORDER BY ne suffit pas, sauf si votre menu_order correspond à votre ordre chronologique.

Je ne peux pas prendre le crédit pour cela, mais j'ai trouvé le code suivant dans this Gist :

<?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 ne modifiez que la clause ORDER BY, la requête recherche toujours les publications supérieures ou inférieures à la date de publication actuelle. Si vos messages ne sont pas dans l'ordre chronologique, vous n'obtiendrez pas le bon message.

Cela modifie la clause where pour rechercher les publications où menu_order est supérieur ou inférieur au menu_order de la publication actuelle, en plus de la modification de la clause orderby.

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

21
jjeaton
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' );
4
Micheal Jess

J'ai essayé de m'accrocher sans succès. Peut-être juste un problème de 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; ?>
4
Szabolcs Páll

Basé sur @Szabolcs Páll's answer J'ai créé cette classe d'utilitaires avec des méthodes d'assistance pour pouvoir obtenir les publications de type par ordre de menu et obtenir le message suivant et précédent par ordre de menu. J'ai également ajouté des conditions pour vérifier si le message en cours 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;
    }

}
0
Eli Jayson

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 message précédent et suivant au message actuel. Il accepte les arguments à utiliser dans la classe WP_Query.

0
any_h

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

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

0
Philip