ลิงก์ถัดไป / ก่อนหน้าโพสต์สามารถสั่งซื้อได้ตามลำดับเมนูหรือโดยเมตาคีย์


32

ฉันมีชุดบทความที่เรียงตามค่า meta_key พวกเขายังสามารถจัดเรียงตามเมนูถ้าจำเป็น

ลิงก์โพสต์ถัดไป / ก่อนหน้า (สร้างโดยnext_post_link, previous_post_linkหรือposts_nav_linkทั้งหมดนำทางตามลำดับเหตุการณ์ในขณะที่ฉันเข้าใจพฤติกรรมเริ่มต้นนี้ฉันไม่เข้าใจวิธีการเปลี่ยนแปลงฉันพบว่ามันแมปผ่านไปยัง จากนั้นจะเริ่มดูเหมือนรหัสยากพอสมควรแนะนำให้เขียนใหม่ตั้งแต่ต้นเพื่อแทนที่หรือมีทางออกที่ดีกว่า


2
นี่คือปลั๊กอินที่สมบูรณ์แบบสำหรับปัญหาของคุณ: wordpress.org/support/topic/... wordpress.org/extend/plugins/...ขอบคุณ Ambrosite! :)
miguelb

1
โปรดทราบว่าคำตอบที่สองดูเหมือนจะให้ผลลัพธ์ที่ถูกต้อง
โทมัส

คำตอบ:


29

ทำความเข้าใจเกี่ยวกับการฝึกงาน

คำสั่ง "เรียงลำดับ" ของโพสต์ถัดไป (ก่อนหน้า / ถัดไป) ไม่ใช่การเรียงลำดับ "เรียง" จริงๆ มันเป็นแบบสอบถามที่แยกต่างหากในแต่ละคำขอ / หน้าแต่มันเรียงลำดับแบบสอบถามโดยpost_date- หรือผู้ปกครองโพสต์ถ้าคุณมีการโพสต์ลำดับชั้นเป็นวัตถุที่ปรากฏในปัจจุบัน

เมื่อคุณดูที่ internals ของ 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()- ตัวอย่างใน Codex โดยทั่วไปฟังก์ชั่นนี้ใช้เพื่อสร้างคำสั่ง meta 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) ขนาดเล็กหรือในไฟล์ themes.php ของคุณและเปลี่ยนฟังก์ชั่นขึ้นอยู่กับ$adjacent = $previous ? 'previous' : 'next';ตัวแปรและ$order = $previous ? 'DESC' : 'ASC';ตัวแปร:

ชื่อตัวกรองที่แท้จริง

ดังนั้นชื่อตัวกรองคือ:

  • get_previous_post_join, get_next_post_join
  • get_previous_post_where, get_next_post_where
  • get_previous_post_sort, get_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) หากทำอะไรเช่นนี้สำหรับการสืบค้น meta ให้ตรวจสอบget_meta_sql()
Stephen Harris

+1 ถึงคุณ @StephenHarris! ไม่เคยเห็นหน้านี้มาก่อน คำถามสั้น ๆ : เมื่อฉันอ่านจากแหล่งที่คุณต้องผ่านวัตถุแบบสอบถามที่ผ่านการรับรองคุณจะทำอย่างไรกับตัวกรองที่กล่าวถึงข้างต้น ตราบใดที่ฉันเห็นว่ามีเฉพาะคิวรีสตริงที่ส่งผ่านเนื่องจากเคียวรีถูกเรียกใช้หลังจากตัวกรอง
ไกเซอร์

2
Nope, $meta_queryเป็นเพียงอาร์เรย์ที่คุณจะส่งผ่านไปWP_Queryสำหรับmeta_queryอาร์กิวเมนต์: ในตัวอย่างนี้: $meta_sql = get_meta_sql( $meta_query, 'post', $wpdb->posts, 'ID');- นี้สร้างJOINและWHEREเป็นส่วนหนึ่งของแบบสอบถามที่จะต้องมีการเพิ่ม
สตีเฟ่นแฮร์ริส

@StephenHarris ช่วงเวลาที่สมบูรณ์แบบในการแก้ไขคำตอบ (ของฉัน) หนึ่งคำ
ไกเซอร์

@StephenHarris ฉันมีปัญหาในการใช้ผลลัพธ์ของ get_meta_sql () - คุณสามารถช่วยในการเข้าร่วมจุด?
Jodi Warren

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 แบบสอบถามจะยังคงค้นหาโพสต์ที่มากกว่าหรือน้อยกว่าวันที่โพสต์ปัจจุบัน หากโพสต์ของคุณไม่ได้เรียงตามลำดับเวลาคุณจะไม่ได้รับโพสต์ที่ถูกต้อง

สิ่งนี้จะเปลี่ยนตำแหน่งที่จะค้นหาโพสต์โดยที่ menu_order มากกว่าหรือน้อยกว่า menu_order ของโพสต์ปัจจุบันนอกเหนือจากการแก้ไขคำสั่งย่อยโดยคำสั่งย่อย

ส่วนคำสั่ง orderby นั้นไม่ควรฮาร์ดโค้ดเพื่อใช้ DESC เนื่องจากจะต้องสลับตามว่าคุณได้รับลิงค์โพสต์ถัดไปหรือก่อนหน้า


3
หนึ่งหมายเหตุ: ข้อมองหาWHERE 'YYYY-mm-dd HH:mm:ss'หากไม่เป็นไปตามนั้นจะไม่ทำงาน เนื่องจากค่าไม่ได้ถูกกำหนดโดย DB แต่โดยแอปพลิเคชันคุณจะต้องตรวจสอบรูปแบบนั้นก่อนเมื่อสร้างนิพจน์ทั่วไป
ไกเซอร์

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_where, get_previous_post_joinและget_previous_post_sortการเล่นที่ดีกับประเภทโพสต์ที่กำหนดเองและการสั่งซื้อที่ซับซ้อนที่มีคีย์ meta ผมให้ขึ้นและใช้วิธีนี้ ขอบคุณ!
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

ตามคำตอบของ @Szabolcs Pá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-adighbor-post-link/link

WP_Query 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-adighbor-posts-in-wordpress


-1

ฉันได้พบวิธีที่ง่ายกว่ามากในการบรรลุการนำทางตามโพสต์เมตา - คีย์โดยไม่จำเป็นต้องปรับเปลี่ยนฟังก์ชั่น

ตัวอย่างของฉัน: คุณมี 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เมื่อ codex ระบุว่าไม่ควรใช้
Pieter Goosen

แต่มันได้ผล ดังนั้นทางเลือกคือ WP_Query หรืออะไร
Kent Miller

ใช่WP_Queryควรใช้เหมือนคำตอบก่อนหน้า
Pieter Goosen

1
@ KentMiller มีไดอะแกรมที่ให้ข้อมูลในหน้า codexและคุณอาจพบว่าคำถามนี้มีประโยชน์ การทำความคุ้นเคยกับการประชุมเหล่านี้มีค่ามาก
Jodi Warren
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.