Как я могу создать целевую страницу категории, за которой следуют страницы постов?

9

Я пытаюсь создать шаблон категории в своей теме, который будет отображать статическое содержимое, если пользователь находится на первой странице архива, а затем отображать сообщения в категории на следующих страницах. Я в основном пытаюсь повторить поведение категорий на Wired.com, где http://www.wired.com/category/design - это целевая страница, а / category / design / page / 1 показывает обратный хронологический список таких записей, как вы. ожидать на архиве категории.

Ключевым моментом здесь является то, что я не показываю никаких постов из архива категорий на первой странице, поэтому мне нужна следующая страница, чтобы начать с первого поста. Сначала я попытался сделать это с помощью смещения и ручной разбивки на страницы , установив смещение запроса в 0, если переменная «постраничного» запроса была равна 2. Но смещение игнорируется, когда оно установлено на ноль, поэтому лучшее, что я могу сделать, это установить смещение на 1 и начните со второго поста в категории.

Вот что я добавил в functions.php:

add_action('pre_get_posts', 'category_query_offset', 1 );
function category_query_offset(&$query) {
  // Before anything else, make sure this is the right query
  if (!$query->is_category('news')) {
    return;
  }

  // Next, determine how many posts per page
  $ppp = get_option('posts_per_page');

  //Next, detect and handle pagination
  if ($query->is_paged) {
    // Manually determine page query offset
    $page_offset = (($query->query_vars['paged']-1) * $ppp) - $ppp;
    // Apply adjust page offset
    $query->set('offset', $page_offset );
  }
}

Что было бы лучше, и что, по-видимому, делает Wired , - это использовать нумерацию страниц по умолчанию, чтобы посты начинались на странице 1, но вставляли страницу 0 для статического содержимого. Гипотетически, кажется, что это было бы возможно, отображая статический контент, если «paged» равно 0, и отображая первую страницу постов, если «paged» равен 1. Проблема в том, что «paged» никогда не равен 1, потому что Wordpress устанавливает «paged». 'в ноль, когда пользователь запрашивает первую страницу. Это означает, что «paged» равно 0 для / category / news / page / 1 и / category / news.

Есть ли способ проверить, запрашивал ли пользователь / category / news / page / 1, а не / category / news? В противном случае, есть ли способ отобразить все сообщения в категории, начиная со страницы 2?

willcwelch
источник

Ответы:

7

Это довольно интересный вопрос (за который я проголосовал, особенно за ваш подход и исследования ). Большой кривая здесь - первая страница запроса:

  • Вы не можете настроить запрос на возврат 0сообщений на первой странице

  • Перемещая содержимое каждой страницы вверх на одну страницу, вы потеряете последнюю страницу, поскольку в запросе останется только то же количество сообщений, поэтому $max_num_pagesсвойство останется прежним.

Нам нужно будет как-то «обмануть» WP_Queryкласс, чтобы мы возвращали наши сообщения со смещением в одну страницу, а также получали правильное количество страниц, чтобы не потерять последнюю страницу в запросе.

Давайте посмотрим на следующую идею и попробуем поместить все в код. Прежде чем мы сделаем, я хотел бы сделать несколько замечаний здесь

ВАЖНЫЕ ЗАМЕТКИ:

  • Все не проверено, поэтому может быть глючит. Не забудьте проверить это локально с включенной отладкой

  • Код требует как минимум PHP 5.3, любая версия ниже 5.3 вызовет фатальную ошибку. Обратите внимание, что если вы все еще используете любую версию ниже PHP 5.5, вы должны были уже давно обновиться

  • Измените код и используйте его по своему усмотрению в соответствии с вашими потребностями

ИДЕЯ ЛАМПОЧКИ:

ЧТО НАМ НУЖНО

Чтобы все заработало, нам понадобится следующее:

  • Номер текущей просматриваемой страницы

  • posts_per_pageНабор опции в настройках чтения

  • изготовленный на заказ offset

  • Измените $found_postsсвойство запроса, чтобы исправить $max_num_pagesсвойство

Нумерация страниц WP_Queryсводится к очень простым нескольким строкам кода

if ( empty($q['nopaging']) && !$this->is_singular ) {
    $page = absint($q['paged']);
    if ( !$page )
        $page = 1;
    // If 'offset' is provided, it takes precedence over 'paged'.
    if ( isset( $q['offset'] ) && is_numeric( $q['offset'] ) ) {
        $q['offset'] = absint( $q['offset'] );
        $pgstrt = $q['offset'] . ', ';
    } else {
        $pgstrt = absint( ( $page - 1 ) * $q['posts_per_page'] ) . ', ';
    }
    $limits = 'LIMIT ' . $pgstrt . $q['posts_per_page'];
}

Что в основном происходит, когда явно установлено смещение, pagedпараметр игнорируется. Первый параметр предложения SQL LIMITпересчитывается из смещения и будет количеством сообщений, которые будут пропущены в сгенерированном запросе SQL.

Исходя из вашего вопроса, очевидно, при установке offsetна 0, запрос на смещение не выполняется, что странно, так как следующая проверка должна вернуть true

if ( isset( $q['offset'] ) && is_numeric( $q['offset'] ) )

0является действительным числом и должно возвращать true. Если это не в вашей установке, вы должны отладить проблему

Чтобы вернуться к данной проблеме, мы будем использовать ту же логику для вычисления и установки нашего смещения, чтобы получить сообщение 1 на странице 2 и оттуда разбить запрос на страницы. Для первой страницы мы не будем ничего менять, поэтому посты, которые, как предполагается, должны быть на странице 1, все еще будут на странице как обычно, нам просто нужно будет позже «спрятать» их, чтобы мы не отображали их на странице 1

add_action( 'pre_get_posts', function ( $q )
{
    if (    !is_admin() // Only target the front end, VERY VERY IMPORTANT
         && $q->is_main_query() // Only target the main query, VERY VERY IMPORTANT
         && $q->is_cateory( 'news' ) // Only target the news category
    ) {
        $current_page = $q->get( 'paged' ); // Get the current page number
        // We will only need to run this from page 2 onwards
        if ( $current_page != 0 ) { // You can also use if ( is_paged() ) {
            // Get the amount of posts per page
            $posts_per_page = get_option( 'posts_per_page' );
            // Recalculate our offset
            $offset = ( ( $current_page - 1) * $posts_per_page ) - $posts_per_page; // This should work on page 2 where it returns 0

            // Set our offset
            $q->set( 'offset', $offset );
        }
    }
});

Вы должны видеть те же сообщения со страницы 1 на странице 2. Как я уже говорил ранее, если этого не происходит, либо is_numeric( 0 )возвращается false ( чего не должно ), либо у вас есть другое pre_get_postsдействие, которое также пытается установить смещение, или вы используют использование posts_*фильтров предложений ( точнее, post_limitsфильтра ). Это будет то, что вам нужно будет отладить самостоятельно.

Следующая проблема заключается в исправлении нумерации страниц, поскольку, как я уже говорил, у вас будет короткая страница. Для этого нам нужно будет добавить значение get_option( 'posts_per_page' )к количеству сообщений, найденных в запросе, поскольку мы компенсируем запрос на эту сумму. делая это, мы эффективно добавляем 1в $max_num_pagesсобственность.

add_action( 'found_posts', function ( $found_posts, $q )
{
    if (    !is_admin() // Only target the front end, VERY VERY IMPORTANT
         && $q->is_main_query() // Only target the main query, VERY VERY IMPORTANT
         && $q->is_cateory( 'news' ) // Only target the news category
    ) {
        $found_posts = $found_posts + get_option( 'posts_per_page');
    }
}, 10, 2 );

Это должно отсортировать все, кроме первой страницы.

ВСЕ ВМЕСТЕ СЕЙЧАС ( и специально для @ialocin - Желтая подводная лодка )

Это все должно идти в functions.php

add_action( 'pre_get_posts', function ( $q )
{
    if (    !is_admin() // Only target the front end, VERY VERY IMPORTANT
         && $q->is_main_query() // Only target the main query, VERY VERY IMPORTANT
         && $q->is_cateory( 'news' ) // Only target the news category
    ) {
        $current_page = $q->get( 'paged' ); // Get the current page number
        // We will only need to run this from page 2 onwards
        if ( $current_page != 0 ) { // You can also use if ( is_paged() ) {
            // Get the amount of posts per page
            $posts_per_page = get_option( 'posts_per_page' );
            // Recalculate our offset
            $offset = ( ( $current_page - 1) * $posts_per_page ) - $posts_per_page; // This should work on page 2 where it returns 0

            // Set our offset
            $q->set( 'offset', $offset );
        }
    }
});

add_filter( 'found_posts', function ( $found_posts, $q )
{
    if (    !is_admin() // Only target the front end, VERY VERY IMPORTANT
         && $q->is_main_query() // Only target the main query, VERY VERY IMPORTANT
         && $q->is_cateory( 'news' ) // Only target the news category
    ) {
        $found_posts = $found_posts + get_option( 'posts_per_page');
    }
    return $found_posts;
}, 10, 2 );

ВАРИАНТЫ ПЕРВОЙ СТРАНИЦЫ

Здесь есть несколько вариантов:

ОПЦИЯ 1

Скорее всего, я бы пошел на этот вариант. То, что вы хотели бы сделать здесь, это создать category-news.php( если вы еще этого не сделали ). Это будет шаблон, который будет использоваться при newsпросмотре категории. Этот шаблон будет очень просто

пример

<?php
get_header()

if ( !is_paged() ) { // This is the first page
    get_template_part( 'news', 'special' );
} else { // This is not the first page
    get_template_part( 'news', 'loop' );
}

get_sidebar();
get_footer();

Как видите, я включил две части шаблона, news-special.phpи news-loop.php. Основы двух пользовательских шаблонов:

  • news-special.php-> Эта часть шаблона будет тем, что вы хотите отобразить на первой странице. Добавьте всю свою пользовательскую статическую информацию здесь. Будьте очень осторожны, чтобы не вызывать цикл в этом шаблоне, так как при этом будут отображаться сообщения первой страницы.

  • news-loop.php-> Это шаблон, где мы будем вызывать цикл. Этот раздел будет выглядеть примерно так:

    global $wp_query;
    while ( have_posts() ) {
    the_post();
    
        // Your template tags and markup
    
    }

ВАРИАНТ 2

Создайте отдельный шаблон со своим статическим содержимым и просто используйте category_templateфильтр, чтобы использовать этот шаблон, когда мы просматриваем первую страницу newsкатегории. Также убедитесь, что в этом шаблоне не вызывается цикл по умолчанию. Кроме того, убедитесь, что ваше соглашение об именах не противоречит именам шаблонов в иерархии шаблонов.

Я надеюсь, что это полезно. Не стесняйтесь терпеть комментарии с проблемами

РЕДАКТИРОВАТЬ

Благодаря OP, в WP_Queryклассе есть определенная ошибка , проверьте trac ticket # 34060 . Код, который я разместил, взят из Wordpress v4.4, и ошибка исправлена ​​в этой версии.

Я вернулся к исходному коду v4.3, где есть ошибка, и я могу подтвердить, что 0игнорируется, когда устанавливается значение в offsetкачестве, поскольку код просто проверяет, offsetявляется ли параметр empty. 0считается пустым в PHP. Я не уверен, что это поведение (ошибка) встречается только в v4.3 или во всех предыдущих версиях (согласно заявке, эта ошибка есть в v4.3), но есть патч для этой ошибки, который вы можете проверить в трак билет. Как я уже сказал, эта ошибка определенно исправлена ​​в v4.4

Питер Гусен
источник
2
Спасибо за ваш подробный ответ. Это на самом деле в значительной степени то, что я реализовал в моей первой попытке. Проблема заключалась в том, что установка «смещения» на 0 не работала. Оказывается, это была ошибка в ядре WordPress core.trac.wordpress.org/ticket/34060 . Исправление заставило все работать. Единственное, чего не хватает в вашем коде, это return $found_posts;после оператора if в действии found_posts. Спасибо!
Willcwelch
Спасибо за ваш положительный отзыв. Я вернулся к исходному коду версии 4.3 query.php, и да, 0он будет проигнорирован, потому что здесь проверка пуста и 0считается пустой. Код в моем ответе - это код обновления, который поставляется с v4.4, поэтому я могу подтвердить, что ошибка исправлена ​​в 4.4. Я обновлю свой ответ соответственно. По found_postsвопросу, хороший улов, это на самом деле фильтр и должен быть возвращен. Наслаждайтесь
Питер Гусен
ха-ха, спасибо :) и всем, кто задается вопросом, почему ... раз, два, три, четыре, можно мне еще немного ... фактическая ссылка на песню все вместе
Николай
@ialocin хахаха, я запомню это ;-)
Питер Гусен
1
это довольно броско :) но неудивительно, потому что это в значительной степени написано как детская песня
Николай
0

Обновить:

add_action('pre_get_posts', 'category_query_offset', 1 );
function category_query_offset($query) {
 // Before anything else, make sure this is the right query
 if (!$query->is_category('news')) {
    return;
 }

 // Next, determine paged
 $ppp = get_option('paged');
}
// Next, handle paged
if($ppp > 1){
    $query->set('paged', $ppp-1);
}

Попробуйте что-то вроде

$paged = ( get_query_var('paged') ) ? get_query_var('paged')-1 : 1;
$args = array (
    'paged' => $paged,
);
Kika
источник
Я исправил свое объяснение того, как работает переменная paged. Похоже, что вы пытаетесь уменьшить номер каждой страницы на 1, поэтому запрос на страницу 2 дает вам страницу 1. Проблема в том, что при запросе страницы 1 для paged установлено значение 0. Кроме того, get_query_var () следует использовать вместо get_option для 'paged'. С этим исправлением, когда это выполняется, каждый раз, когда запрос изменяется, он повторяется, пока «paged» не станет 0 для любого запроса страницы.
Willcwelch