¿Se pueden ordenar los enlaces Siguiente / Anterior Publicar por orden de menú o por una meta tecla?

32

Tengo una serie de publicaciones ordenadas por un valor meta_key. También se pueden organizar por orden de menú, si es necesario.

Los enlaces de publicación siguiente / anterior (generados por next_post_link, previous_post_linko posts_nav_linktodos navegan por cronología. Si bien entiendo este comportamiento predeterminado, no entiendo cómo cambiarlo. Descubrí que se asigna a adyacente_post_link en link-template.php, pero entonces comienza a parecer bastante codificado. ¿Se recomienda volver a escribir esto desde cero para reemplazarlo, o hay una mejor solución?

Jodi Warren
fuente
2
Aquí está el complemento perfecto para su problema: wordpress.org/support/topic/… wordpress.org/extend/plugins/… ¡ Gracias Ambrosite! :)
miguelb
1
Tenga en cuenta que la segunda respuesta parece dar el resultado correcto.
Thomas

Respuestas:

29

Entendiendo lo interno

El orden de "clasificación" de las publicaciones adyacentes (siguiente / anterior) no es realmente un "orden" de clasificación. Es una consulta separada en cada solicitud / página, pero clasifica la consulta por post_date- o la publicación principal si tiene una publicación jerárquica como objeto visualizado actualmente.

Cuando echas un vistazo a las partes internas de next_post_link(), entonces ves que es básicamente un contenedor API para adjacent_post_link(). La función posterior llama get_adjacent_post()internamente con el $previousargumento / indicador configurado bool(true|false)para tomar el enlace de publicación siguiente o anterior.

¿Qué filtrar?

Después de profundizar en él, verá que el get_adjacent_post() enlace Fuente tiene algunos filtros agradables para su salida (también conocido como resultado de la consulta): (Nombre del filtro / Argumentos)

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

Entonces puedes hacer mucho con eso. Eso comienza con el filtrado de la WHEREcláusula, así como la JOINtabla ed y la ORDER BYdeclaración.

El resultado se almacena en memoria caché para la solicitud actual, por lo que no agrega consultas adicionales si llama a esa función varias veces en una sola página.

Consulta automática de construcción

Como @StephenHarris señaló en los comentarios, hay una función central que puede ser útil al crear la consulta SQL: get_meta_sql()- Ejemplos en Codex . Básicamente, esta función solo se usa para construir la instrucción meta SQL que se usa WP_Query, pero también puede usarla en este caso (u otros). El argumento que arroja en él es una matriz, exactamente la misma que se agregaría a a WP_Query.

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

El valor de retorno es una matriz:

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

Para que pueda usar $sql['join']y $sql['where']en su devolución de llamada.

Dependencias a tener en cuenta

En su caso, lo más fácil sería interceptarlo en un pequeño complemento (mu) o en su archivo de funciones themes.php y modificarlo según la $adjacent = $previous ? 'previous' : 'next';variable y la $order = $previous ? 'DESC' : 'ASC';variable:

Los nombres de filtro reales

Entonces los nombres de filtro son:

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

Envuelto como un complemento

... y la devolución de llamada del filtro sería (por ejemplo) algo así como lo siguiente:

<?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' );
emperador
fuente
2
+1. Solo para obtener información, (@magnakai) si hace algo como esto para meta consultas, consulteget_meta_sql()
Stephen Harris el
¡+1 a ti @StephenHarris! No he visto este antes. Pregunta corta: como leí de la fuente que tiene que pasar un objeto de consulta totalmente calificado, ¿cómo haría esto con los filtros mencionados anteriormente? Por lo que puedo ver, solo se pasan cadenas de consulta, ya que la consulta se ejecuta después de los filtros.
Kaiser
2
no, $meta_queryes solo la matriz a la que pasaría WP_Querypor el meta_queryargumento: En este ejemplo: $meta_sql = get_meta_sql( $meta_query, 'post', $wpdb->posts, 'ID');- esto genera la parte JOINy la WHEREparte de la consulta que debería agregarse.
Stephen Harris el
@StephenHarris Momento perfecto para editar una (mi) respuesta.
kaiser
@StephenHarris, tengo problemas para aplicar la salida de get_meta_sql (). ¿Pueden ayudarme a unir los puntos?
Jodi Warren
21

La respuesta de Kaiser es impresionante y exhaustiva, sin embargo, solo cambiar la cláusula ORDER BY no es suficiente a menos que menu_ordercoincida con su orden cronológico.

No puedo tomar crédito por esto, pero encontré el siguiente código en esta esencia :

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

He modificado los nombres de funciones para WP.SE.

Si solo cambia la cláusula ORDER BY, la consulta aún busca publicaciones mayores o menores que la fecha de publicación actual. Si sus publicaciones no están en orden cronológico, no obtendrá la publicación correcta.

Esto cambia la cláusula where para buscar publicaciones donde el menu_order es mayor o menor que el menú_order de la publicación actual, además de modificar la cláusula orderby.

La cláusula orderby tampoco debería estar codificada para usar DESC, ya que tendrá que cambiar en función de si está obteniendo el enlace de publicación siguiente o anterior.

jjeaton
fuente
3
Una nota: la WHEREcláusula busca 'YYYY-mm-dd HH:mm:ss'. Si eso no se cumple, no funcionará. Como el DB no establece el valor, sino la Aplicación, primero deberá verificar ese formato al generar la expresión Regular.
kaiser
5

Intenté conectar sin éxito. Puede ser solo un problema de mi configuración, pero para aquellos que no pueden hacer que el gancho funcione, esta es la solución más 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
fuente
Después de algunas horas de intentar conseguirlo get_previous_post_where, get_previous_post_joiny get_previous_post_sortde jugar bien con los tipos de publicaciones personalizadas y los pedidos complejos que incluyen meta teclas, me di por vencido y usé esto. ¡Gracias!
squarecandy
Lo mismo aquí, no solo quería ordenar por Orden de menú, sino también buscar publicaciones con una meta_key y meta_value específica, por lo tanto, este era el mejor método. El único cambio que hice fue envolverlo en una función.
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
fuente
1

Basado en la respuesta de @Szabolcs Páll, he creado esta clase de utilidad con métodos auxiliares para poder obtener publicaciones de tipo por orden de menú y obtener la publicación siguiente y anterior por orden de menú también. Además, agregué condiciones para verificar si la publicación actual es la primera o la última publicación para obtener la última o la primera publicación respectivamente.

Por ejemplo:

// $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 clase completa:

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
fuente
0

Encuentro este pequeño complemento realmente útil: http://wordpress.org/plugins/wp-query-powered-adarest-post-link/

WP_Query Powered Ad adyacente Post Link es un complemento para desarrolladores. Agrega la función wpqpapl();a WordPress que puede devolver información sobre la publicación anterior y siguiente a la actual. Acepta argumentos para su uso en la WP_Queryclase.

any_h
fuente
0

Esto funcionó para mí:

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

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

Philip
fuente
-1

He encontrado una manera mucho más fácil de lograr una navegación de publicación basada en meta-clave, sin la necesidad de modificar functions.php.

Mi ejemplo: tienes un products.php y quieres cambiar entre productos. El producto anterior es el siguiente más barato, el siguiente producto es el siguiente más caro.

Aquí viene mi solución para 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
fuente
-10 por esta respuesta. ¿Cómo puede ser esta una mejor solución si está usando query_postscuando el códice establece que no debe usarse?
Pieter Goosen
pero funciona. Entonces, ¿la alternativa es WP_Query o qué?
Kent Miller
Sí, WP_Querydebe usarse como en respuestas anteriores.
Pieter Goosen
1
@KentMiller, hay un diagrama informativo en la página del códice , y también puede encontrar esta pregunta útil. Vale la pena familiarizarse con estas convenciones.
Jodi Warren