Next / Prev Post linkleri menü sırasına göre veya bir meta anahtar ile sipariş edilebilir mi?


32

Meta_key değerine göre sıralanan bir dizi yayınım var. Gerekirse menü sırasına göre de düzenlenebilirler.

Tarafından üretilen bir sonraki / önceki sonrası bağlantılar ( next_post_link, previous_post_linkya posts_nav_linkben bu varsayılan davranışı anlamakla birlikte tarihsel olarak bütün gezinmek., Ben bağlantı template.php içinde adjacent_post_link üzerine eşleştiren buldum. Bunu nasıl değiştirileceğini anlıyorum, ama yok daha sonra oldukça zor kodlanmış gibi görünmeye başlar, bunu değiştirmek için sıfırdan tekrar yazmanız önerilir mi, yoksa daha iyi bir çözüm var mı?


2
Sorununuz için mükemmel bir eklenti: wordpress.org/support/topic/… wordpress.org/extend/plugins/… Teşekkürler Ambrosite! :)
miguelb

1
İkinci cevabın doğru sonucu verdiğini görün.
Thomas

Yanıtlar:


29

İç organları anlamak

Bitişik (sonraki / önceki) yazıların "sıralama" sırası gerçekten bir "sıra" değildir. Her istek / sayfada ayrı bir sorgu olmakla birlikte , post_dategösterilmekte olan nesne olarak hiyerarşik bir gönderim varsa , sorguyu - veya gönderen ebeveyne göre sıralar.

İçlerine baktığınızda, next_post_link()bunun temelde bir API sarmalayıcısı olduğunu görürsünüz adjacent_post_link(). Daha sonraki işlev , sonraki veya önceki yazı bağlantısını almak için ayarlanan argüman / bayrak get_adjacent_post()ile dahili olarak çağırır .$previousbool(true|false)

Ne süzülmeli?

Daha derine indikten sonra, get_adjacent_post() Source link'in çıktısı için bazı güzel filtrelere sahip olduğunu göreceksiniz (aka sorgu sonucu): (Filter Name / Arguments)

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

Yani onunla çok şey yapabilirsiniz . Bu, WHEREcümlenin yanı sıra JOINed tablosu ve ORDER BYifadeyi filtrelemekle başlar .

Sonuç, geçerli istek için belleğe önbelleğe alınır; bu nedenle, bu işlevi tek bir sayfada birden çok kez çağırırsanız ek sorgular eklemez.

Otomatik sorgu oluşturma

As @StephenHarris Açıklamalarda belirttiği SQL sorgusu oluştururken kullanışlı olabilir bir çekirdek fonksiyonu vardır: get_meta_sql()- Örnekler Kodeksi . Temel olarak, bu işlev yalnızca kullanılan meta SQL ifadesini oluşturmak için kullanılır WP_Query, ancak bu durumda (veya başkalarını) da kullanabilirsiniz. İçine attığın argüman bir a WP_Query.

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

Dönüş değeri bir dizidir:

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

Böylece $sql['join']ve $sql['where']geri aramada kullanabilirsiniz.

Akılda tutulması gereken bağımlılıklar

Sizin durumunuzda en kolay şey küçük (mu) bir eklenti veya temalar işlevleri.php dosyasında engellemek ve $adjacent = $previous ? 'previous' : 'next';değişkene ve değişkene bağlı olarak değiştirmek olacaktır $order = $previous ? 'DESC' : 'ASC';:

Gerçek filtre adları

Yani filtre isimleri:

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

Eklenti olarak sarılmış

... ve filtre geri çağırma (örneğin) aşağıdaki gibi bir şey olurdu:

<?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. Sadece bilgi için, (@magnakai) meta sorguları için böyle bir şey yapıyorsanız, kontrol edinget_meta_sql()
Stephen Harris

+1 @ StephenHarris! Bunu daha önce görmedim. Kısa soru: Kaynaktan okuduğumda, tam bir sorgulama nesnesini geçmek zorunda olduğunuzu, yukarıda belirtilen filtrelerle nasıl yaparsınız? Görebildiğim kadarıyla, sorgu filtrelerden sonra yürütüldüğü için yalnızca sorgu dizeleri geçiyor.
kaiser

2
hayır, $meta_querysadece , argüman WP_Queryiçin ileteceğiniz dizidir meta_query: argüman: Bu örnekte: $meta_sql = get_meta_sql( $meta_query, 'post', $wpdb->posts, 'ID');- bu, eklenecek sorgunun JOINve WHEREkısmını oluşturur .
Stephen Harris

@StephenHarris Bir (benim) cevabı düzenlemek için mükemmel bir an.
kaiser

@StephenHarris, get_meta_sql () 'nin çıktısını uygulamakta sorun yaşıyorum - noktalara katılmaya yardımcı olabilir misiniz?
Jodi Warren

21

Kaiser'in cevabı harika ve eksiksizdir, ancak menu_orderkronolojik sıranıza uymadıkça ORDER BY yan tümcesini değiştirmek yeterli değildir .

Bunun için kredi alamam, ancak bu kodda aşağıdaki kodu buldum :

<?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 için işlev isimlerini değiştirdim.

SİPARİŞ BY yan tümcesini yalnızca değiştirirseniz, sorgu geçerli posta tarihinden daha büyük veya daha küçük olan gönderileri arar. Gönderileriniz kronolojik sırada değilse, doğru iletiyi alamazsınız.

Bu, order_ yan tümcesini değiştirmenin yanı sıra, menu_order öğesinin geçerli postun menu_order değerinden daha büyük veya daha küçük olduğu gönderileri aramak için where yan tümcesini değiştirir.

Ayrıca, bir sonraki ya da önceki posta bağlantısına sahip olup olmadığına bağlı olarak geçiş yapmanız gerekeceğinden, sipariş cümlesi DESC'yi kullanmak için kodlanmış olmamalıdır.


3
Bir not: WHEREfıkra arar 'YYYY-mm-dd HH:mm:ss'. Eğer bu gerçekleşmezse işe yaramaz. Değer DB tarafından ayarlanmadığından, ancak Uygulama tarafından, Düzenli ifadeyi oluştururken önce bu biçimi kontrol etmeniz gerekir.
kaiser

5

Başarı olmadan bağlamaya çalıştı. Yapılandırmamın bir sorunu olabilir, ancak kancayı çalıştıramayanlar için en basit çözüm:

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

almaya çalışırken bir kaç saat sonra get_previous_post_where, get_previous_post_joinve get_previous_post_sortözel yayın türleri ve meta tuşları içerir karmaşık sipariş ile oynamak-güzel vazgeçtim ve bu kullandı. Teşekkürler!
squarecandy

Burada da sadece Menü Sırasına göre sipariş vermek istemedim, aynı zamanda belirli bir meta_key ve meta_value olan gönderileri de aradım, bu yüzden bu en iyi yöntemdi. Yaptığım tek değişiklik bir işleve sarmaktı.
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 Pall en dayanarak cevap ben menü emriyle Çeşidi mesajları almak ve menü emriyle sonraki ve önceki posta almak hem de muktedir yardımcı yöntemleriyle bu yardımcı sınıf oluşturduk. Ayrıca, geçerli gönderinin sırasıyla son veya ilk gönderiyi alan ilk gönderi olup olmadığını kontrol etmek için koşullar ekledim.

Örneğin:

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

Tam sınıf:

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

Bu benim için çalıştı:

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

Alındığı yer: https://stackoverflow.com/questions/16495117/how-to-skip-certain-links-on-adjacent-posts-in-wordpress


-1

Bir üst-anahtar tabanlı gezinme sonrası navigasyon işlevini değiştirmeye gerek kalmadan ulaşmak için çok daha kolay bir yol buldum.

Örneğim: Bir products.php'niz var ve ürünler arasında geçiş yapmak istiyorsunuz. Önceki ürün bir sonraki ucuz ürün, sonraki ürün bir sonraki daha pahalı ürün.

Single.php için çözümüm geliyor :

<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 bu cevap için. query_postsKodeks kullanılmaması gerektiğini belirtirken bu nasıl daha iyi bir çözüm olabilir?
Pieter Goosen

ama işe yarıyor. yani alternatif WP_Query ya da ne?
Kent Miller

Evet, WP_Queryönceki cevaplarda olduğu gibi kullanılmalıdır.
Pieter Goosen

1
@KentMiller, kodeks sayfasında bilgilendirici bir şema var ve bu soruyu kullanışlı bulabilirsiniz . Kendinizi bu sözleşmelerle tanıştırmaya değer.
Jodi Warren
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.