Next / Prev Postリンクは、メニュー順またはメタキー順に並べることができますか?


32

meta_keyの値で並べられた一連の投稿があります。必要に応じて、メニュー順に並べることもできます。

生成された次/の前のポストのリンク(next_post_linkprevious_post_link、またはposts_nav_link年表すべて移動します。私は、このデフォルトの動作を理解しながら、私はそれを変更する方法を理解していない。私はそれがリンクtemplate.phpにadjacent_post_linkに至るマップすることがわかったが、それはかなりハードコードされているように見え始めます。


2
ここにあなたの問題のための完全なプラグインです:wordpress.org/support/topic/...の wordpress.org/extend/plugins/...はあなたにAmbrositeをありがとう!:)
miguelb

1
2番目の答えは正しい結果をもたらすように見えることに注意してください。
トーマス

回答:


29

内部を理解する

隣接する(次/前)投稿の「ソート」順序は、実際にはソート「順序」ではありません。リクエスト/ページごとに個別のクエリです、クエリをpost_date-または現在表示されているオブジェクトとして階層的な投稿がある場合は投稿の親で並べ替えます。

の内部next_post_link()を見ると、基本的にのAPIラッパーであることがわかりますadjacent_post_link()。後の関数は、次または前の投稿リンクを取得するように設定されget_adjacent_post()$previous引数/フラグで内部的に呼び出しますbool(true|false)

フィルタリングするもの

さらに深く掘り下げた後、get_adjacent_post() ソースリンクにはその出力(クエリ結果)のためのいくつかの素晴らしいフィルターがあることがわかります:(フィルター名/引数)

  • "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およびedテーブルと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']あなたのコールバックインチ

留意すべき依存関係

あなたの場合、最も簡単なのは、小さな(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' );

2
+1。ただ、情報、(@magnakai)メタクエリのためにこのような何かをやっている場合、チェックアウトのためにget_meta_sql()
スティーブン・ハリス

+1 @StephenHarris!これを見たことがありません。簡単な質問:完全修飾クエリオブジェクトを渡す必要があることをソースから読みましたが、上記のフィルターを使用してこれをどのように行いますか?私が見る限り、クエリはフィルタの後に実行されるため、クエリ文字列のみが渡されます。
カイザー

2
いや、$meta_queryあなたに渡すのと同じ配列であるWP_Queryためmeta_query、引数:この例では:$meta_sql = get_meta_sql( $meta_query, 'post', $wpdb->posts, 'ID');-これは、生成JOINしてWHERE追加する必要がありますクエリの一部を。
スティーブンハリス

@StephenHarris 1つの(私の)回答を編集する完璧な瞬間。
カイザー

@ StephenHarris、get_meta_sql()の出力の適用に問題があります-ドットを結合するのを手伝ってもらえますか?
ジョディウォーレン

21

カイザーの答えは素晴らしく、徹底的ですが、menu_order時系列の順序に一致しない限り、ORDER BY句を変更するだけでは十分ではありません。

私はこれを信用できませんが、この要点で次のコードを見つけました:

<?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句のみを変更しても、クエリは現在の投稿日よりも大きいまたは小さい投稿を探します。投稿が時系列順になっていないと、正しい投稿を取得できません。

これにより、whereby句の変更に加えて、menu_orderが現在の投稿のmenu_orderよりも大きいまたは小さい投稿を探すようにwhere句が変更されます。

また、次の投稿リンクを取得するか、前の投稿リンクを取得するかに基づいて切り替える必要があるため、DESCを使用するためにorderby句をハードコーディングしないでください。


3
注:WHERE句はを探し'YYYY-mm-dd HH:mm:ss'ます。それが満たされない場合、動作しません。値はDBによってではなく、アプリケーションによって設定されるため、正規表現を作成するときは、最初にその形式を確認する必要があります。
カイザー14

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カスタムポストタイプとメタキーが含まれて複雑な順序で素敵を再生するために、私はあきらめ、これを使用していました。ありがとう!
squarecandy

ここでも同じように、メニューの順序で並べ替えるだけでなく、特定の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

この小さなプラグインは本当に便利だと思います:http : //wordpress.org/plugins/wp-query-powered-adjacent-post-link/

WP_Query Powered Adjacent Post Linkは、開発者向けのプラグインです。wpqpapl();WordPressに関数を追加し、現在の前の投稿と次の投稿に関する情報を返すことができます。WP_Queryクラスで使用する引数を受け入れます。


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コーデックスが使用すべきでないと述べているときに使用している場合、これはどのように優れたソリューションになりますか?
ピーターグースン

しかし、それは動作します。代替案はWP_Queryまたは何ですか?
ケントミラー

はい、WP_Query前の回答のように使用する必要があります。
ピーターグースン

1
@KentMiller、コーデックスページに参考図があります。また、この質問が役立つ。これらの規則をよく理解する価値があります。
ジョディウォーレン
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.