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

30

J'ai une série de publications qui sont classées par une valeur meta_key. Ils peuvent é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 navigent tous par chronologie. Bien que je comprenne ce comportement par défaut, je ne comprends pas comment le changer. mappe vers adjacent_post_link dans link-template.php, mais cela commence à sembler plutôt codé en dur. Est-il recommandé de réécrire ceci à partir de zéro pour le remplacer ou existe-t-il une meilleure solution?

    
posée Jodi Warren 19.11.2012 - 18:23
la source

7 réponses

27

Comprendre les éléments internes

L’ordre de "tri" des posts adjacents (suivant / précédent) n’est pas vraiment un "ordre". Il s'agit d'une requête distincte sur chaque requête / page, mais il 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 en fait d'un wrapper d'API pour < a href="http://queryposts.com/function/adjacent_post_link/"> adjacent_post_link() . La dernière fonction appelle get_adjacent_post() en interne avec $previous argument / flag défini sur bool(true|false) pour saisir l'élément suivant ou précédent. poster un lien.

Que filtrer?

Après avoir approfondi le sujet, vous verrez que get_adjacent_post() Le lien source contient quelques filtres intéressants (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 beaucoup avec cela. Cela commence par le filtrage de la clause WHERE , ainsi que de la table JOIN ed et de l’instruction ORDER BY .

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

Création automatique de requêtes

Comme @StephenHarris dans les commentaires, il existe une fonction essentielle qui pourrait s'avérer utile lorsque construction de la requête SQL: get_meta_sql() - Exemples dans le Codex . Fondamentalement, cette fonction est simplement utilisée pour construire la déclaration meta 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 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 filtre actuels

Les noms de filtre 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 de 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' );
    
réponse donnée kaiser 19.11.2012 - 18:40
la source
18

La réponse de Kaiser est géniale et complète, mais modifier la clause ORDER BY ne suffit pas, à moins que votre menu_order correspond à votre ordre chronologique.

Je ne peux pas en accepter le crédit, mais j'ai trouvé le code suivant dans cette explication :

<?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 messages ne sont pas classés par ordre chronologique, vous ne recevrez pas le bon message.

Ceci 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 en fonction du type de lien suivant ou précédent.

    
réponse donnée jjeaton 16.04.2013 - 05:18
la source
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' );
    
réponse donnée Micheal Jess 20.06.2013 - 07:00
la source
2

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; ?>
    
réponse donnée Szabolcs Páll 26.10.2017 - 12:38
la source
0

Je trouve ce petit plugin très utile: enlace

  

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 .

    
réponse donnée any_h 26.11.2013 - 19:17
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 )";
}

extrait de: enlace

    
réponse donnée Philip 12.03.2015 - 15:22
la source
-1

J'ai trouvé un moyen beaucoup plus simple de réaliser une navigation par publication basée sur une méta-clé, sans qu'il soit nécessaire de modifier functions.php.

Mon exemple: vous avez un fichier products.php et vous souhaitez basculer entre les produits. Le produit précédent est le prochain produit le moins cher, le produit suivant le produit le 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 } ?>
    
réponse donnée Kent Miller 21.05.2014 - 10:25
la source

Lire d'autres questions sur les étiquettes