问题描述

我有一系列由 meta_key 值排序的帖子。如果需要,也可以通过菜单顺序进行排列。

下一个/上一页的链接 (由 next_post_linkprevious_post_linkposts_nav_link 生成) 都是按时间顺序导航的虽然我明白这个默认行为,但我不明白如何改变它,我发现它在 link-template.php 中映射到 adjacent_post_link,但是它开始看起来好像是 hard-coded,推荐 re-write 从头开始替代它,还是有更好的解决方案。

最佳解决方案

了解内部

“sort” 相邻 (下一个/上一个) 帖子的顺序不是真的一个”order” 。它是每个请求/页面上的一个单独的查询,但是如果您有一个层次结构的帖子作为当前显示的对象,它会排列 post_date 的查询。

当您查看 next_post_link()的内部组件时,您会看到它基本上是 adjacent_post_link()的 API 封装。后来的函数在 get_adjacent_post()内部调用 $previous 参数/标志设置为 bool(true|false)来抓取下一个或上一个后链接。

什么要过滤?

在深入挖掘之后,您会看到 get_adjacent_post()源链接的输出有一些不错的过滤器 (a.k.a. 查询结果):(过滤器名称/参数)

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

所以你可以做很多事情。首先过滤 WHERE 子句,以及 JOIN 表和 ORDER BY 语句。

结果将缓存在内存中用于当前请求,因此如果在单个页面上多次调用该函数,则不会添加其他查询。

自动查询建立

正如 @StephenHarris 在注释中指出的那样,构建 SQL 查询时可能会派上用场的核心功能:get_meta_sql() – 示例 in Codex 。基本上这个函数只是用来构建 WP_Query 中使用的元 SQL 语句,但是在这种情况下也可以使用它 (或其他) 。你抛出的参数是一个数组,与 WP_Query 相同。

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

返回值是一个数组:

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

所以你可以在你的回调中使用 $sql['join']$sql['where']

依赖关系要记住

在你的情况下,最简单的事情是在一个小的 (mu) 插件或主题 functions.php 文件中截取它,并根据 $adjacent = $previous ? 'previous' : 'next'; 变量和 $order = $previous ? 'DESC' : 'ASC'; 变量进行更改:

实际的过滤器名称

所以过滤器名称是:

  •  get_previous_post_joinget_next_post_join

  •  get_previous_post_whereget_next_post_where

  •  get_previous_post_sortget_next_post_sort

封装成插件

… 和过滤器回调将是 (例如) 类似于以下内容:

<?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’s answer 是非常棒和彻底的,但只要更改 ORDER BY 子句是不够的,除非您的 menu_order 符合您的时间顺序。

我不能信用这个,但是我在 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' );

我修改了 WP.SE 的函数名。

如果只更改了 ORDER BY 子句,则查询仍然查找大于或小于当前发布日期的帖子。如果您的帖子不按时间顺序排列,您将无法获得正确的信息。

除了修改 orderby 子句之外,这会更改 where 子句以查找 menu_order 大于或小于当前帖子的 menu_order 的帖子。

orderby 子句也不应该被硬编码以使用 DESC,因为它将需要根据您是否获得下一个或上一个链接进行切换。

第三种解决方案

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

参考文献

注:本文内容整合自 Google/Baidu/Bing 辅助翻译的英文资料结果。如果您对结果不满意,可以加入我们改善翻译效果:薇晓朵技术论坛。