Các liên kết Next / Prev Post có thể được sắp xếp theo thứ tự menu hoặc bởi một phím meta không?


32

Tôi có một loạt các bài đăng được sắp xếp theo giá trị meta_key. Họ cũng có thể được sắp xếp theo thứ tự thực đơn, nếu cần thiết.

Các liên kết bài đăng tiếp theo / trước (được tạo bởi next_post_link, previous_post_linkhoặc posts_nav_linktất cả điều hướng theo niên đại. Trong khi tôi hiểu hành vi mặc định này, tôi không hiểu cách thay đổi nó. Tôi thấy rằng nó ánh xạ tới liền kề_post_link trong link-template.php, nhưng sau đó nó bắt đầu có vẻ khá khó mã hóa. Có nên viết lại từ đầu để thay thế nó không, hay có một giải pháp nào tốt hơn.


2
Đây là plugin hoàn hảo cho vấn đề của bạn: wordpress.org/support/topic/ Khăn wordpress.org/extend/plugins/ cảm ơn bạn Ambrosite! :)
Miguelb

1
Lưu ý rằng câu trả lời thứ hai dường như mang lại kết quả chính xác.
Thomas

Câu trả lời:


29

Hiểu nội bộ

Thứ tự "sắp xếp" của các bài viết liền kề (tiếp theo / trước) không thực sự là một "thứ tự" sắp xếp. Đó là một truy vấn riêng biệt trên mỗi yêu cầu / trang, nhưng nó sắp xếp truy vấn theo post_date- hoặc phụ huynh bài đăng nếu bạn có một bài đăng phân cấp như đối tượng hiện được hiển thị.

Khi bạn nhìn vào phần bên trong next_post_link(), thì bạn sẽ thấy rằng về cơ bản nó là một trình bao bọc API cho adjacent_post_link(). Hàm sau gọi get_adjacent_post()nội bộ với $previousđối số / cờ được đặt bool(true|false)để lấy liên kết bài đăng tiếp theo hoặc trước đó.

Lọc cái gì?

Sau khi tìm hiểu sâu hơn về nó, bạn sẽ thấy get_adjacent_post() liên kết Nguồn có một số bộ lọc đẹp cho đầu ra của nó (còn gọi là kết quả truy vấn): (Tên bộ lọc / Đối số)

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

Vì vậy, bạn có thể làm rất nhiều với nó. Điều đó bắt đầu với việc lọc WHEREmệnh đề, cũng như JOINbảng ed và ORDER BYcâu lệnh.

Kết quả được lưu trong bộ nhớ cho yêu cầu hiện tại, do đó, nó không thêm các truy vấn bổ sung nếu bạn gọi hàm đó nhiều lần trên một trang.

Xây dựng truy vấn tự động

Như @StephenHarris đã chỉ ra trong các bình luận, có một chức năng cốt lõi có thể có ích khi xây dựng Truy vấn SQL: get_meta_sql()- Ví dụ trong Codex . Về cơ bản, hàm này chỉ được sử dụng để xây dựng câu lệnh meta SQL được sử dụng WP_Query, nhưng bạn cũng có thể sử dụng nó trong trường hợp này (hoặc những thứ khác). Đối số mà bạn ném vào nó là một mảng, chính xác sẽ thêm vào a WP_Query.

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

Giá trị trả về là một mảng:

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

Vì vậy, bạn có thể sử dụng $sql['join']$sql['where']trong cuộc gọi lại của bạn.

Phụ thuộc vào ghi nhớ

Trong trường hợp của bạn, điều dễ nhất là chặn nó trong một plugin (mu) nhỏ hoặc trong tệp tin.php của chủ đề và thay đổi tùy thuộc vào $adjacent = $previous ? 'previous' : 'next';biến và $order = $previous ? 'DESC' : 'ASC';biến:

Tên bộ lọc thực tế

Vì vậy, tên bộ lọc là:

  • get_previous_post_join, get_next_post_join
  • get_previous_post_where, get_next_post_where
  • get_previous_post_sort, get_next_post_sort

Được gói như một plugin

... và cuộc gọi lại bộ lọc sẽ là (ví dụ) một cái gì đó như sau:

<?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. Chỉ để biết thông tin, (@magnakai) nếu làm điều gì đó như thế này cho các truy vấn meta, hãy xemget_meta_sql()
Stephen Harris

+1 cho bạn @StephenHarris! Chưa từng thấy cái này trước đây. Câu hỏi ngắn: Khi tôi đọc từ nguồn mà bạn phải vượt qua một đối tượng truy vấn đủ điều kiện, bạn sẽ làm điều này với các bộ lọc được đề cập ở trên như thế nào? Theo như tôi có thể thấy chỉ có các chuỗi truy vấn được thông qua, vì truy vấn được thực thi sau các bộ lọc.
kaiser

2
Không, $meta_querychỉ là mảng bạn sẽ truyền WP_Querycho meta_queryđối số, trong ví dụ này: $meta_sql = get_meta_sql( $meta_query, 'post', $wpdb->posts, 'ID');- điều này tạo ra JOINWHEREmột phần của truy vấn cần được thêm vào.
Stephen Harris

@StephenHarris Khoảnh khắc hoàn hảo để chỉnh sửa một câu trả lời (của tôi).
kaiser

@StephenHarris, tôi gặp sự cố khi áp dụng đầu ra của get_meta_sql () - bạn có thể giúp tham gia chấm không?
Jodi Warren

21

Câu trả lời của Kaiser là tuyệt vời và kỹ lưỡng, tuy nhiên chỉ cần thay đổi mệnh đề ORDER BY là không đủ trừ khi bạn menu_orderkhớp với thứ tự thời gian của bạn.

Tôi không thể lấy tín dụng cho việc này, nhưng tôi đã tìm thấy đoạn mã sau trong ý chính này :

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

Tôi đã sửa đổi tên hàm cho WP.SE.

Nếu bạn chỉ thay đổi mệnh đề ORDER BY, truy vấn vẫn tìm kiếm các bài đăng lớn hơn hoặc nhỏ hơn ngày đăng hiện tại. Nếu bài đăng của bạn không theo thứ tự thời gian, bạn sẽ không nhận được đúng bài.

Điều này thay đổi mệnh đề where để tìm các bài đăng trong đó menu_order lớn hơn hoặc nhỏ hơn menu_order của bài đăng hiện tại, ngoài việc sửa đổi mệnh đề orderby.

Mệnh đề orderby cũng không nên được mã hóa cứng để sử dụng DESC vì nó sẽ cần phải chuyển đổi dựa trên việc bạn đang nhận được liên kết bài đăng tiếp theo hay trước đó.


3
Một lưu ý: WHEREMệnh đề tìm kiếm 'YYYY-mm-dd HH:mm:ss'. Nếu điều đó không được đáp ứng, nó sẽ không hoạt động. Vì giá trị không được DB đặt, nhưng bởi Ứng dụng, trước tiên bạn sẽ phải kiểm tra định dạng đó khi xây dựng biểu thức Chính quy.
kaiser

5

Đã thử móc vào mà không thành công. Có thể chỉ là một vấn đề về cấu hình của tôi, nhưng đối với những người không thể làm cho hook hoạt động, đây là giải pháp đơn giản nhất:

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

sau một vài giờ cố gắng để có được get_previous_post_where, get_previous_post_joinget_previous_post_sortđể chơi đẹp với bài tùy loại và đặt hàng phức tạp bao gồm các phím meta, tôi đã từ bỏ và sử dụng này. Cảm ơn!
squarecandy

Tương tự ở đây, tôi không chỉ muốn đặt hàng theo Menu Order, mà còn tìm kiếm các bài đăng có meta_key và meta_value cụ thể, do đó đây là phương pháp tốt nhất. Sự thay đổi duy nhất tôi đã thực hiện là bọc nó thành một hàm.
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

Dựa trên câu trả lời của @Szabolcs Páll Tôi đã tạo lớp tiện ích này bằng các phương thức trợ giúp để có thể nhận các bài đăng theo thứ tự menu và nhận bài đăng tiếp theo và trước đó theo thứ tự menu. Tôi cũng đã thêm các điều kiện để kiểm tra xem bài đăng hiện tại là bài đăng đầu tiên hoặc cuối cùng để có được bài đăng cuối cùng hoặc đầu tiên.

Ví dụ:

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

Cả lớp

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

Tôi thấy plugin nhỏ này thực sự tiện dụng: http://wordpress.org/plugins/wp-query-powered-adjighbor-post-link/

Liên kết bài viết liền kề được cung cấp bởi WP_Query là một plugin dành cho nhà phát triển. Nó bổ sung chức năng wpqpapl();cho WordPress có thể trả lại thông tin về bài viết trước và bài tiếp theo về hiện tại. Nó chấp nhận các đối số để sử dụng trong WP_Querylớp.


0

Điều này làm việc cho tôi:

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

Lấy từ: https://stackoverflow.com/questions/16495117/how-to-skip-certain-links-on-adjighbor-posts-in-wordpress


-1

Tôi đã tìm thấy một cách dễ dàng hơn nhiều để đạt được điều hướng bài dựa trên meta-key, mà không cần phải sửa đổi hàm.php.

Ví dụ của tôi: Bạn có một sản phẩm.php và bạn muốn chuyển đổi giữa các sản phẩm. Sản phẩm trước là sản phẩm rẻ hơn tiếp theo, sản phẩm tiếp theo đắt hơn tiếp theo.

Đây là giải pháp của tôi cho 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 cho câu trả lời này. Làm thế nào đây có thể là một giải pháp tốt hơn nếu bạn đang sử dụng query_postskhi codex nói rằng nó không nên được sử dụng.
Pieter Goosen

nhưng nó đã có tác dụng. Vậy phương án thay thế là WP_Query hay sao?
Kent Miller

Có, WP_Querynên được sử dụng như trong câu trả lời trước.
Pieter Goosen

1
@KentMiller, có một sơ đồ thông tin trên trang codex và bạn cũng có thể thấy câu hỏi này có ích. Thật đáng để làm quen với những quy ước này.
Jodi Warren
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.