Bisakah tautan Next / Sebelumnya dikirim berdasarkan pesanan menu atau dengan meta key?


32

Saya memiliki serangkaian posting yang diurutkan berdasarkan nilai meta_key. Mereka juga bisa diatur oleh urutan menu, jika perlu.

Tautan pos berikutnya / sebelumnya (dihasilkan oleh next_post_link,, previous_post_linkatau posts_nav_linksemua dinavigasi oleh kronologi. Meskipun saya memahami perilaku default ini, saya tidak mengerti bagaimana mengubahnya. Saya menemukan bahwa ia memetakan melalui proximity_post_link di link-template.php, tetapi kemudian mulai tampak agak sulit dikodekan. Dianjurkan untuk menulis ulang ini dari awal untuk menggantinya, atau apakah ada solusi yang lebih baik.


2
Ini adalah plugin yang sempurna untuk masalah Anda: wordpress.org/support/topic/… wordpress.org/extend/plugins/… Terima kasih Ambrosite! :)
miguelb

1
Perhatikan bahwa jawaban kedua tampaknya menghasilkan hasil yang benar.
Thomas

Jawaban:


29

Memahami internal

Urutan "sort" dari posting yang berdekatan (berikutnya / sebelumnya) sebenarnya bukan "urutan". Ini adalah kueri terpisah pada setiap permintaan / halaman, tetapi mengurutkan kueri oleh post_date- atau induk tulisan jika Anda memiliki posting hierarkis sebagai objek yang saat ini ditampilkan.

Ketika Anda melihat internal next_post_link(), maka Anda melihat bahwa itu pada dasarnya adalah pembungkus API adjacent_post_link(). Fungsi nanti memanggil secara get_adjacent_post()internal dengan $previousargumen / bendera diatur ke bool(true|false)untuk mengambil tautan posting berikutnya atau sebelumnya.

Apa yang harus disaring?

Setelah menggali lebih dalam ke dalamnya, Anda akan melihat bahwa get_adjacent_post() tautan Sumber memiliki beberapa filter bagus untuk hasilnya (alias hasil kueri): (Nama Filter / Argumen)

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

Jadi Anda bisa melakukan banyak hal dengannya. Itu dimulai dengan memfilter WHEREklausa, serta JOINtabel ed dan ORDER BYpernyataan.

Hasilnya di-cache dalam memori untuk permintaan saat ini, sehingga tidak menambah kueri tambahan jika Anda memanggil fungsi itu beberapa kali pada satu halaman.

Pembuatan kueri otomatis

Seperti @StephenHarris tunjukkan dalam komentar, ada fungsi inti yang mungkin berguna ketika membangun SQL Query: get_meta_sql()- Contoh dalam Codex . Pada dasarnya fungsi ini hanya digunakan untuk membangun pernyataan meta SQL yang digunakan WP_Query, tetapi Anda dapat menggunakannya dalam kasus ini (atau orang lain) juga. Argumen yang Anda lemparkan ke dalamnya adalah array, sama persis yang akan ditambahkan ke WP_Query.

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

Nilai kembali adalah array:

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

Jadi, Anda dapat menggunakan $sql['join']dan $sql['where']dalam panggilan balik Anda.

Ketergantungan yang perlu diingat

Dalam kasus Anda, hal yang paling mudah adalah mencegatnya dalam plugin kecil (mu) atau dalam file functions.php tema Anda dan mengubahnya tergantung pada $adjacent = $previous ? 'previous' : 'next';variabel dan $order = $previous ? 'DESC' : 'ASC';variabel:

Nama filter yang sebenarnya

Jadi nama filternya adalah:

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

Dibungkus sebagai plugin

... dan panggilan balik filter akan menjadi (misalnya) sesuatu seperti berikut ini:

<?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. Sekadar informasi, (@magnakai) jika melakukan hal seperti ini untuk pertanyaan meta, periksaget_meta_sql()
Stephen Harris

+1 untuk Anda @StephenHarris! Belum pernah melihat yang ini sebelumnya. Pertanyaan singkat: Ketika saya membaca dari sumber bahwa Anda harus melewati objek permintaan yang sepenuhnya memenuhi syarat, bagaimana Anda melakukan ini dengan filter yang disebutkan di atas? Sejauh yang saya bisa lihat, hanya ada string kueri yang terlewati, karena kueri dieksekusi setelah filter.
kaiser

2
tidak, $meta_queryhanya array yang akan Anda berikan WP_Queryuntuk meta_queryargumen,: Dalam contoh ini: $meta_sql = get_meta_sql( $meta_query, 'post', $wpdb->posts, 'ID');- ini menghasilkan JOINdan WHEREbagian dari kueri yang perlu ditambahkan.
Stephen Harris

@StephenHarris Momen sempurna untuk mengedit satu jawaban (saya).
kaiser

@StephenHarris, saya mengalami kesulitan menerapkan output get_meta_sql () - dapatkah Anda membantu bergabung dengan titik-titik?
Jodi Warren

21

Jawaban Kaiser luar biasa dan teliti, namun hanya mengubah klausa ORDER BY tidak cukup kecuali Anda menu_ordercocok dengan urutan kronologis Anda.

Saya tidak dapat mengambil kredit untuk ini, tetapi saya menemukan kode berikut di intisari ini :

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

Saya telah memodifikasi nama fungsi untuk WP.SE.

Jika Anda hanya mengubah klausa ORDER BY, kueri masih mencari posting yang lebih besar atau kurang dari tanggal posting saat ini. Jika posting Anda tidak dalam urutan kronologis, Anda tidak akan mendapatkan posting yang tepat.

Ini mengubah klausa mana untuk mencari posting di mana menu_order lebih besar dari atau kurang dari menu_order posting saat ini, di samping memodifikasi klausa orderby.

Klausa orderby juga tidak boleh dikodekan untuk menggunakan DESC karena harus beralih berdasarkan apakah Anda mendapatkan tautan posting berikutnya atau sebelumnya.


3
Satu catatan: WHEREKlausa mencari 'YYYY-mm-dd HH:mm:ss'. Jika itu tidak terpenuhi, itu tidak akan berhasil. Karena nilainya tidak ditentukan oleh DB, tetapi oleh Aplikasi, Anda harus memeriksa format itu terlebih dahulu saat membangun ekspresi Reguler.
kaiser

5

Mencoba masuk tanpa hasil. Mungkin hanya masalah konfigurasi saya, tetapi bagi mereka yang tidak dapat membuat hook bekerja, berikut adalah solusi paling sederhana:

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

setelah beberapa jam mencoba untuk mendapatkan get_previous_post_where, get_previous_post_joindan get_previous_post_sortbermain bagus dengan tipe posting khusus dan pemesanan kompleks yang mencakup kunci meta, saya menyerah dan menggunakan ini. Terima kasih!
squarecandy

Sama di sini, saya tidak hanya ingin memesan dengan Urutan Menu, tetapi juga mencari posting dengan meta_key dan meta_value tertentu, oleh karena itu ini adalah metode terbaik. Satu-satunya perubahan yang saya lakukan adalah membungkusnya menjadi sebuah fungsi.
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

Berdasarkan jawaban @Szabolcs Páll, saya telah membuat kelas utilitas ini dengan metode pembantu untuk bisa mendapatkan postingan dari tipe berdasarkan urutan menu dan mendapatkan postingan berikutnya dan sebelumnya berdasarkan urutan menu juga. Saya juga menambahkan kondisi untuk memeriksa apakah posting saat ini adalah posting pertama atau terakhir untuk mendapatkan posting terakhir atau pertama masing-masing.

Sebagai contoh:

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

Kelas lengkap:

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

Ini bekerja untuk saya:

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

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


-1

Saya telah menemukan cara yang jauh lebih mudah untuk mencapai navigasi posting berbasis meta-key, tanpa perlu memodifikasi functions.php.

Contoh saya: Anda memiliki products.php dan Anda ingin beralih antar produk. Produk sebelumnya adalah yang lebih murah berikutnya, produk berikutnya yang berikutnya lebih mahal.

Inilah solusi saya untuk 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 untuk jawaban ini. Bagaimana ini bisa menjadi solusi yang lebih baik jika Anda menggunakan query_postsketika codex menyatakan itu tidak boleh digunakan.
Pieter Goosen

tapi berhasil. jadi alternatifnya adalah WP_Query atau apa?
Kent Miller

Ya, WP_Queryharus digunakan seperti pada jawaban sebelumnya.
Pieter Goosen

1
@ CentMiller, ada diagram informatif pada halaman codex , dan Anda juga mungkin menemukan pertanyaan ini berguna. Sebaiknya biasakan diri Anda dengan konvensi ini.
Jodi Warren
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.