WordPress实现在归档范围内查找相邻文章

多年前,贝总曾经问过我,能不能增加一个“上一篇”、“下一篇”在某某类别里跳转的功能。

狭义上说,get_previous_post()/get_next_post()/get_post_navagation()这三个函数都有“in_same_term”,“excluded_terms”,“taxonomy”这三个参数。配合使用,可以实现在相同类别和标签范围内跳转的功能。
但是,这几个函数使用起来非常死板,意味着某个类别或者标签的文章,相邻文章就只能在该种别内部迭代,永远跳不出圈外。
而且,这几个默认函数对搜索结果,日期归档的结果也毫无办法。

所谓“归档范围内查找相邻文章”是这样的:
当读者点击过某种类别的归档或者搜索关键字,取得过文章列表后,点击任意文章标题后,获取的相邻文章均在之前的文章列表范围之内。
直到读者点击不属于这种类别的文章链接或者文章集合链接后,退出之前的特殊范围,回归时间顺序的相邻文章。
这种特定的类别集合,可以是年/月/日/作者的归档,可以是某个类别或者标签,也可以是搜索结果。
比如点击“2010年12月”,进入一个20篇文章的归档页面。从这个页面上点击任何一篇文章开始阅读。那么从此开始的“上一篇”“下一篇”将始终限定在这20篇文章的范围内。直到点击主页或者直接跳到随机文章后,再不限定在20篇文章的范围内。

之前其实已经搞过一个方案,通过cookie识别不同读者,然后通过向数据库写入临时数据的方式记录结果集。这么做是因为WordPress并不支持Session,页面跟页面之间只能通过数据库进行数据传递,所以效果非常不理想,一直没公布。
直到前几天,忽然想通了,WordPress虽然不支持Session,但PHP支持啊……
于是问题迎刃而解。核心思路是在用户的浏览过程中,记录临时变量。

下面是具体代码:

第一步:开启SESSION

function demo_register_session()
{
  if( !session_id() )
  {
    session_start();
  }
}

add_action('init', 'demo_register_session');

第二步:在进入归档类页面时记录文章集,超出前回结果范围时清除记录

function demo_keep_query(){
    global $wp_query;

    if (isset($_SESSION['last_tax'])) {
        $old_tax = $_SESSION['last_tax'];//上一次的记录结果
    }
    else {
        $old_tax = '';
    }
    $new_tax='';
    if (is_search()||is_archive()) {
        if ( is_search() ){
            $new_tax = "搜索结果:" . get_search_query( false ) ;
        }
        else if ( is_category() ){
            $new_tax = "分类:" . single_cat_title( '', false );
        }
        else if ( is_tag() ){
            $new_tax = "标签:" . single_tag_title( '', false );
        }
        else if ( is_year() ) {
            $new_tax = "年:" . get_the_date('Y') ;
        }
        else if ( is_month() ) {
            $new_tax = "月:" . get_the_date('F Y');
        }
        else if ( is_day() ) {
            $new_tax = "日:" . get_the_date(get_option('date_format'));
        }
        else {
            //其它归档类型不处理且清空之前的数据。
            //应该是剩下一个is_author()。这个类型对于只有一个作者的博客没意义,多人博客可以仿照上面的格式添加。
            $_SESSION['last_tax'] = '';
            $_SESSION['tax_ids'] = array();
            return;
        }
        if ($new_tax != $old_tax) {
            //新结果集与上一次不一致
            $vars = $wp_query->query_vars;
            $myquery = new WP_Query( $vars );
            $vars['order'] = "ASC";//所有archive类的默认排序都是时间降序的,不更改的话会导致“上一篇”“下一篇”正好相反
            $vars['posts_per_page'] = 9999;//官方文档说可以设-1,实际测试设-1会出错,还是设成正的吧。改变这个值,让所有结果出现在一页里。
            if ($myquery->post_count == 1 && $myquery->max_num_pages == 1){
                wp_reset_postdata();//如果只有一条则没必要存
                $_SESSION['last_tax'] = '';
                $_SESSION['tax_ids'] = array();
                return;
            }
            //保存临时数据
            $_SESSION['last_tax'] = $new_tax;
            $_SESSION['tax_ids'] = wp_list_pluck( $myquery->posts, 'ID' );//存结果的ID
            wp_reset_postdata();//恢复默认循环体
        }
    }
    else if (!is_single()) {
        //is_attach()之类的,清空
        $_SESSION['last_tax'] = '';
        $_SESSION['tax_ids'] = array();
    }
    else {
        //single
        $ID = get_the_ID();
        if (empty($old_tax)||!isset($_SESSION['tax_ids'])||count($_SESSION['tax_ids']) == 0) {
            //上一次没有记录,说明在主循环内,什么都不做。
            return;
        }      
        if (FALSE===array_search($ID, $_SESSION['tax_ids'])) {
            //有记录,但是当前post不在其范围内,应该跳出结果。
            $_SESSION['last_tax'] = '';
            $_SESSION['tax_ids'] = array();
            return;
        }
    }
}
add_action('template_redirect', 'demo_keep_query', 9 );//优先度略高于默认钩子,避免意外

第三步:增加取文章的函数。

get_previous_post()/get_next_post()/get_post_navagation()这三个函数允许加钩子的时机非常不合适,不如重写。
三个函数不用全部写,针对自己的主题,用哪个写哪个。

function demo_get_prev_post() {
    if (isset($_SESSION['last_tax'])&& isset($_SESSION['tax_ids']) && count($_SESSION['tax_ids'])>1) {
        $ID = get_the_ID();
        $pos = array_search($ID, $_SESSION['tax_ids']);//找当前post_ID在结果中的位置
        if ( FALSE === $pos ) {
            return NULL;
        }
        $count = count($_SESSION['tax_ids']);
        if ( $pos > 0) {
            return get_post($_SESSION['tax_ids'][$pos -1]);//取前一篇
        }
        else {
            return NULL;
        }
    }
    else{
        return get_previous_post();
    }  
}

function demo_get_next_post() {
    if (isset($_SESSION['last_tax'])&& isset($_SESSION['tax_ids'])&& count($_SESSION['tax_ids'])>1) {
        $ID = get_the_ID();
        $pos = array_search($ID, $_SESSION['tax_ids']);//找当前post_ID在结果中的位置
        if ( FALSE === $pos ) {
            return NULL;
        }
        $count = count($_SESSION['tax_ids']);
        if ( $pos < $count - 1) {
            return get_post($_SESSION['tax_ids'][$pos +1]);//取后一篇
        }
        else {
            return NULL;
        }
    }
    else{
        return get_next_post();
    } 
}

function demo_get_post_navagation($args=array()){
   $args = wp_parse_args( $args, array(
        'prev_text'          => '%title',
        'next_text'          => '%title',
        'screen_reader_text' => '文章导航',
    ) );
    /*
    'in_same_term'       => false,
    'excluded_terms'     => '',
    'taxonomy'           => 'category',
    这三个参数被忽略。
     */

    if ( !is_single() ){
        return;
    }

    $ID = get_the_ID();
    if (isset($_SESSION['last_tax'])&& isset($_SESSION['tax_ids'])&& count($_SESSION['tax_ids'])>1) {
        $pos = array_search($ID, $_SESSION['tax_ids']);
        if ( FALSE === $pos ) {
            the_post_navigation($args);
            return;
        }
        $count = count($_SESSION['tax_ids']);
        $next_id = 0;
        $previous_id = 0;
        $previous="";
        $next="";
        if ( $pos < $count - 1) {
            $next_id = $_SESSION['tax_ids'][$pos +1];
        }
        if ($pos > 0 ) {
            $previous_id = $_SESSION['tax_ids'][$pos -1];
        }
        if ($previous_id > 0)
        {
            $previous = str_replace( '%title', get_the_title( $previous_id ), $args['prev_text'] );
            $previous = '<a href="'.get_permalink( $previous_id).'" rel="prev">'.$previous.'</a>';
            $previous = '<div class="nav-previous">'.$previous.'</div>';
        }
        if ($next_id > 0)
        {
            $next = str_replace( '%title', get_the_title( $next_id ), $args['next_text'] );
            $next = '<a href="'.get_permalink( $next_id ).'" rel="next">'.$next.'</a>';
            $next = '<div class="nav-next">'.$next.'</div>';
        }
        if ( "" === $desc = $_SESSION['last_tax'] )
        {
            $desc = $args['screen_reader_text'];
        }
        $navigation = _navigation_markup( $previous . $next, 'post-navigation', $desc );
        echo $navigation;
    }
    else{
        the_post_navigation($args);
        return;
    } 
}

第四步:修改主题

用demo_XXXX替换single.php导航部分原有的函数即可。代码略。

==== UPDATE 2019/11/12 ====
代码更新,修正了“上一篇”“下一篇”顺序正好相反的问题。

已有12条评论

  1. 不断的摸索可还行

  2. 博主的网站还是很不错的,不过更新要有节奏。还有就是博主,我建议你,网站的界面可以适当修整一下,例如,块的边框可以去掉,这样整站看起来才不会花,才会大气。今天看了你一篇文章解决了一些问题,谢谢。

    1. 坚决不改。

      1. 垃圾评论,我也收到了……

        1. 我刚才还认真回复了,没想到竟然是一模一样的垃圾评论,等会回头就删除了

          1. “坚决不改”就是我的认真回复。

          2. +1.。。。

      2. 这傻叉,到处发广告,在我那里也是这一句,一模一样。。。

        1. 人肉的垃圾,不能自动防也正常。

  3. 话说归档的折叠,
    年份处点了加号后会出现莫名的向上抽搐的动画后才会展开

    1. 那是故意的……好吧,我抄的原效果就是那样。我本人没什么感觉。
      既然你觉得不舒服,那我找时间改掉好了。

      1. 哈哈,只是觉得奇怪,到是没有感觉到什么不适。

  4. 这主题跟之前那个差不多的色调,就我个人来看,还是进步了o(* ̄︶ ̄*)o

    1. 所谓的趋势不就是低对比灰了吧唧的么。其实这就是再之前的那个主题又改了套色。

  5. 体验了一下,很不错,改天把干掉的上一篇下一篇弄回来。

    1. 再丰富一点可以加一个js的退出功能,懒得弄了。

      1. js 折腾不来。
        折腾完才想起开了静态缓存……

        1. 有静态缓存说明有访问量。

  6. 你的白背景里竟然还有DIO

    1. 丰富着呢,我偷了15部漫画的图。在post里能出现13种。

你好,新朋友。留言前请先填写昵称邮箱