問題描述

我有一系列由 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 輔助翻譯的英文資料結果。如果您對結果不滿意,可以加入我們改善翻譯效果:薇曉朵技術論壇。