Чи можна посилання "Наступне / Попереднє повідомлення" упорядкувати в порядку меню або мета-клавішею?


32

У мене є ряд публікацій, упорядкованих за значенням meta_key. Вони також можуть бути упорядковані за порядком меню, якщо це необхідно.

Поштові посилання наступна / попередня (породжувані next_post_link, previous_post_linkабо posts_nav_linkвсе навігації по хронології. У той час як я розумію , це поведінка по замовчуванню, я не розумію , як змінити його. Я виявив , що він відображає до adjacent_post_link на засланні-template.php, але тоді воно починає здаватися досить жорстким. Чи рекомендується переписати це з нуля, щоб замінити його, чи є краще рішення.


2
Ось ідеальний плагін для вашої проблеми: wordpress.org/support/topic/… wordpress.org/extend/plugins/… Дякую, Ambrosite! :)
miguelb

1
Зауважте, що друга відповідь, здається, дає правильний результат.
Томас

Відповіді:


29

Розуміння внутрішніх справ

Порядок "сортування" сусідніх (наступних / попередніх) публікацій насправді не є своєрідним "порядком". Це окремий запит для кожного запиту / сторінки, але він сортує запит по post_date- або батьківському запису, якщо у вас є ієрархічна публікація як відображається в даний час об'єкт.

Коли ви подивитесь на внутрішні пристрої next_post_link(), то побачите, що це в основному обгортка API adjacent_post_link(). Пізніша функція викликає get_adjacent_post()внутрішньо з встановленим $previousаргументом / прапором, bool(true|false)щоб захопити наступне або попереднє посилання.

Що фільтрувати?

Заглибившись у нього, ви побачите, що get_adjacent_post() джерело посилання має декілька приємних фільтрів для його виведення (aka результат запиту): (Назва фільтра / аргументи)

  • "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 . В основному ця функція використовується просто для створення мета-оператора 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), якщо ви робите щось подібне для мета-запитів, перевіртеget_meta_sql()
Стівен Харріс

+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 () - чи можете ви допомогти приєднатися до крапок?
Джоді Уоррен

21

Відповідь Кайзера є приголомшливою та ґрунтовною, однак просто зміни пункту ЗАМОВЛЕННЯ БЕЗ достатньо, якщо ваш 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.

Якщо ви зміните лише пункт «ЗАМОВИТИ ЗА», запит все ще шукатиме публікації, що перевищують поточну дату публікації або менше. Якщо ваші публікації не в хронологічному порядку, ви не отримаєте потрібної публікації.

Це змінює пункт де шукати повідомлення, у яких меню_порядок більше або менше, ніж меню_порядку поточної публікації, на додаток до зміни пункту замовлення.

Застереження порядку замовлення також не повинно бути жорстко кодовано для використання DESC, оскільки його потрібно буде переключити, залежно від того, отримуєте ви наступне або попереднє посилання.


3
Одна примітка: Ця WHEREстаття шукає 'YYYY-mm-dd HH:mm:ss'. Якщо це не виконано, воно не працюватиме. Оскільки значення задається не БД, а програмою, вам доведеться спочатку перевірити цей формат, будуючи регулярний вираз.
кайзер

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грати добре з типами користувальницьких поштових і складного замовлення , який включає в себе мета - ключі, я здався і використовував це. Спасибі!
squarecandy

Тут же не тільки я хотів замовити за допомогою меню замовлення, але й шукати публікації з певними мета-ключами та мета-значеннями, тому це був найкращий метод. Єдина зміна, яку я вніс, - це перетворити його на функцію.
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 Полла відповіді Я створив цей допоміжний клас з методами хелперів , щоб мати можливість отримати повідомлення типу на замовлення меню і отримати наступний і попередній пост на замовлення меню , а також. Я також додав умови, щоб перевірити, чи є поточна публікація першою чи останньою публікацією, щоб отримати останнє чи перше повідомлення відповідно.

Наприклад:

// $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-adjacent-post-link/

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


-1

Я знайшов набагато простіший спосіб досягти поштової навігації, заснованої на мета-ключах, без необхідності змінювати функції.php.

Мій приклад: у вас є 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коли кодекс заявляє, його не слід використовувати.
Пітер Гусен

але це працює. тож альтернативою є WP_Query чи що?
Кент Міллер

Так, WP_Queryслід використовувати, як у попередніх відповідях.
Пітер Гусен

1
@KentMiller, на сторінці кодексу є інформаційна схема , і ви також можете вважати це питання корисним. Варто ознайомитися з цими умовами.
Джоді Уоррен
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.