是否可以按菜单顺序或按meta键对“下一个/上一个帖子”链接进行排序?


32

我有一系列按meta_key值排序的帖子。如有必要,还可以按菜单顺序排列它们。

下一个/上一个帖子链接(由next_post_linkprevious_post_linkposts_nav_link全部按年代顺序导航。虽然我了解此默认行为,但我不知道如何更改它。我发现它映射到link-template.php中的neighbor_post_link,但是建议您从头开始重写它以替换它,或者有更好的解决方案。


2
这是解决您问题的理想插件:wordpress.org/support/topic / ... wordpress.org/extend/plugins / ...谢谢Ambrosite!:)
miguelb

1
请注意,第二个答案似乎产生正确的结果。
托马斯

Answers:


29

了解内部

相邻(下一个/上一个)帖子的“排序”顺序实际上不是排序“顺序”。这是对每个请求/页面的单独查询,但是post_date如果您有一个层次结构的帖子作为当前显示的对象,它将按-或帖子的父级对查询进行排序。

当您查看的内部结构时next_post_link(),您会发现它基本上是的API包装器adjacent_post_link()。后面的函数在get_adjacent_post()内部使用$previous参数/标志设置进行调用,bool(true|false)以获取下一个或上一个发布链接。

要过滤什么?

深入研究之后,您将看到get_adjacent_post() Source链接对其输出(又名查询结果)提供了一些不错的过滤器:(过滤器名称/参数)

  • "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子句,以及JOINed表和ORDER BY语句。

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

自动查询建立

作为@StephenHarris在评论中指出,有一个核心功能构建SQL查询时可能会派上用场:get_meta_sql()-例如在食品法典委员会。基本上,此函数仅用于构建在中使用的元SQL语句WP_Query,但是在这种情况下(或其他情况下)也可以使用它。您放入其中的参数是一个数组,将添加到中的参数完全相同WP_Query

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

返回值是一个数组:

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

因此,您可以在回调中使用$sql['join']$sql['where']

要记住的依赖性

在您的情况下,最简单的方法是将其拦截在一个小型(μ)插件中或在您的themes.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' );

2
+1。仅供参考,(@ magnakai)如果对中继查询执行类似的操作,请查看get_meta_sql()
Stephen Harris

向您+1 @StephenHarris!没看过这个。简短的问题:当我从源中读取到您必须传递完全合格的查询对象时,如何使用上述过滤器来做到这一点?据我所知,仅传递了查询字符串,因为查询是在过滤器之后执行的。
kaiser 2012年

2
不,$meta_query只是你会传递到阵列WP_Querymeta_query,论点:在这个例子中:$meta_sql = get_meta_sql( $meta_query, 'post', $wpdb->posts, 'ID');-此产生JOINWHERE将需要添加查询的一部分。
史蒂芬·哈里斯

@StephenHarris编辑一个(我)答案的完美时机。
kaiser 2012年

@StephenHarris,我在应用get_meta_sql()的输出时遇到了麻烦-您能帮忙点缀吗?
乔迪·沃伦

21

Kaiser的回答真棒而透彻,但是仅更改ORDER BY子句是不够的,除非您menu_order匹配您的时间顺序。

对此不以为然,但是我在要点中找到了以下代码:

<?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,因为它将根据您要获得下一个还是上一个帖子链接进行切换。


3
注意:该WHERE子句查找'YYYY-mm-dd HH:mm:ss'。如果不满足,它将无法正常工作。由于该值不是由数据库设置的,而是由应用程序设置的,因此在构建正则表达式时必须首先检查该格式。
kaiser 2014年

5

试图钩住没有成功。可能只是我的配置问题,但是对于那些无法使挂钩正常工作的人,这是最简单的解决方案:

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

经过几个小时的尝试get_previous_post_whereget_previous_post_join并尝试get_previous_post_sort与自定义帖子类型和包括元键的复杂排序配合使用,我放弃并使用了它。谢谢!
squarecandy18年

同样,在这里,我不仅要按菜单顺序进行排序,而且还要查找具有特定meta_key和meta_value的帖子,因此这是最好的方法。我所做的唯一更改是将其包装为一个函数。
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' );

1

基于@SzabolcsPáll的回答,我已经创建了带有帮助程序方法的实用程序类,以便能够按菜单顺序获取类型的帖子,以及按菜单顺序获取下一个和上一个帖子。我另外添加了条件来检查当前帖子是第一条还是最后一条,以分别获取最新条或第一条帖子。

例如:

// $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

全班:

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

这对我有用:

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

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


-1

我发现了一种无需修改functions.php即可更轻松地实现基于元键的帖子导航的方法。

我的示例:您有一个products.php,并且想要在产品之间切换。上一个产品是下一个更便宜的产品,下一个产品是下一个更昂贵的产品。

这是我的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 } ?>

-10为这个答案。如果您query_posts在法典上规定不应使用它时使用它,这将是一个更好的解决方案。
Pieter Goosen 2014年

但它有效。那么替代方法是WP_Query还是什么?
肯特·米勒

是的,WP_Query应该像以前的答案一样使用。
Pieter Goosen 2014年

1
@KentMiller,法典页面上有一个信息图,您可能还会发现这个问题很方便。熟悉这些约定非常值得。
乔迪·沃伦
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.