Проблема с пользовательским WP_Query и связанной с ним пагинацией/посты_на_страницу

Вопрос или проблема

В моем шаблоне категории (category.php) у меня есть следующий код:

<?php
// Настройка пользовательского цикла — нам нужно исключить избранные посты из списка, чтобы они не повторялись
$paged = get_query_var('paged') ? absint(get_query_var('paged')) : 1;
$args = [
    'post_type' => 'post',
    'cat'=> $current_category->cat_ID,
    'post_status' => 'publish',
    'paged' => $paged,
    'posts_per_page' => $posts_per_page,
    'orderby' => 'date',
    'order' => 'DESC'
];

// Если у нас есть посты для исключения — добавляем этот аргумент
if (!empty($featured_posts_to_exclude)) {
    $args['post__not_in'] = $featured_posts_to_exclude;
}

$category_posts = new WP_Query($args);

if ( $category_posts->have_posts() ) :

    // Используется в части шаблона для изменения контента
    $loop_count = 0;

    /* Начало цикла */
    while ( $category_posts->have_posts() ) : $category_posts->the_post();
            // Используем это вместо "get_template_parts", чтобы передать переменные цикла
            include(locate_template('template-parts/content-horz-ads.php', false, false));
            $loop_count++;
    endwhile;

    // Добавляем пагинацию
    im_numeric_posts_nav($category_posts);

    // Сбрасываем, так как используем пользовательский цикл.
    wp_reset_postdata();

else :

    get_template_part( 'template-parts/content', 'none' );
endif;

Моя функция пагинации следующая (я публикую ее для полноты картины, но она работает совершенно нормально, независимо от того, установлено posts_per_page на 10, или 6, или это пользовательский цикл или нет — она отображает правильные ссылки и количество ссылок):

function im_numeric_posts_nav($custom_query_object = null) {

    // Если мы находимся на отдельной странице, навигация не нужна
    if (is_singular()) {
        return;
    }

    // Если передан пользовательский цикл, используем его...в противном случае используем глобальный цикл
    if ($custom_query_object !== null) {
        $wp_query = $custom_query_object;
    } else {
        global $wp_query;
    }

    /** Останавливаем выполнение, если есть только 1 страница */
    if ($wp_query->max_num_pages <= 1) {
        return;
    }

    $paged = get_query_var('paged') ? absint(get_query_var('paged')) : 1;
    $max = intval($wp_query->max_num_pages);

    /** Добавляем текущую страницу в массив */
    if ($paged >= 1) {
        $links[] = $paged;
    }

    /** Добавляем страницы вокруг текущей в массив */
    if ($paged >= 3) {
        $links[] = $paged - 1;
        $links[] = $paged - 2;
    }

    if (($paged + 2) <= $max) {
        $links[] = $paged + 2;
        $links[] = $paged + 1;
    }

    echo '<div class="pagination"><ul>' . "\n";

    /** Ссылка на предыдущий пост */
    if (get_previous_posts_link('&laquo;')) {
        printf('<li>%s</li>' . "\n", get_previous_posts_link('&laquo;'));
    }

    /** Ссылка на первую страницу, плюс многоточие при необходимости */
    if (!in_array(1, $links)) {
        $class = 1 == $paged ? ' class="active"' : '';

        printf('<li%s><a href="https://wordpress.stackexchange.com/questions/247208/%s">%s</a></li>' . "\n", $class, esc_url(get_pagenum_link(1)), '1');

        if (!in_array(2, $links)) {
            echo '<li>…</li>';
        }
    }

    /** Ссылка на текущую страницу, плюс 2 страницы в обе стороны при необходимости */
    sort($links);
    foreach ((array) $links as $link) {
        $class = $paged == $link ? ' class="active"' : '';
        printf('<li%s><a href="https://wordpress.stackexchange.com/questions/247208/%s">%s</a></li>' . "\n", $class, esc_url(get_pagenum_link($link)), $link);
    }

    /** Ссылка на последнюю страницу, плюс многоточие при необходимости */
    if (!in_array($max, $links)) {
        if (!in_array($max - 1, $links)) {
            echo '<li>…</li>' . "\n";
        }

        $class = $paged == $max ? ' class="active"' : '';
        printf('<li%s><a href="https://wordpress.stackexchange.com/questions/247208/%s">%s</a></li>' . "\n", $class, esc_url(get_pagenum_link($max)), $max);
    }

    /** Ссылка на следующий пост */
    if (get_next_posts_link('&raquo;', $max)) {
        printf('<li>%s</li>' . "\n", get_next_posts_link('&raquo;', $max));
    }

    echo '</ul></div>' . "\n";
}

На страницах категорий с избранными постами, $posts_per_page установлено на 6.

Отображается только 6 постов, но запрос пагинации все равно считает, что на странице 10 постов.

Есть 214 постов, но должно быть только 35 страниц, но если я перейду на любую страницу после 22, я получу 404. Это говорит мне о том, что всё ещё используется значение posts per page, установленное в админке WordPress в разделе Настройки чтения.

Если я изменю это значение на “6” — всё будет замечательно. Однако я не хочу, чтобы это значение по умолчанию было равно 6. Я хочу иметь возможность установить это через переменную posts_per_page в пользовательском запросе.

Есть идеи, почему это происходит, или в чем моя ошибка? Я действительно ломаю голову над этим.

Перестаньте переопределять цикл по умолчанию и используйте pre_get_posts вместо этого. Вот так:

function wpse_247208_custom_category_query( $query ) {
    // Ранний возврат для любой ситуации, которая вас не интересует.
    if ( is_admin() || ! $query->is_main_query() || !is_category() ) {
        return;
    }

    $query->set( 'posts_per_page', 6 );

    // Не используйте глобальные переменные; создайте функцию для заполнения этого значения
    // Я просто не знаю, как вы создали это значение, поэтому хотел убедиться, что оно представлено.
    $featured_posts_to_exclude = get_featured_posts_to_exclude();
    // Если у нас есть посты для исключения — добавляем этот аргумент
    if (!empty($featured_posts_to_exclude)) {
        $query->set('post__not_in', $featured_posts_to_exclude);
    }
}
add_action( 'pre_get_posts', 'hwl_home_pagesize', 1 );

Ответ или решение

Проблема с кастомным WP_Query и пагинацией, похоже, заключается в том, что WordPress использует настройки по умолчанию для параметра posts_per_page, которые задаются в административной панели, независимо от значения, передаваемого в индивидуальном запросе. Это часто связано с нестандартным использованием циклов в шаблонах, что может вызывать конфликт настроек.

Для решения данной проблемы вы можете использовать хук pre_get_posts, который позволяет манипулировать основным запросом WordPress. Это более гибкий и предпочтительный способ для изменения параметров запроса, таких как количество постов на страницу и исключение определённых постов.

Решение проблемы с использованием хука pre_get_posts:

  1. Определение функции-хука:
    Создайте функцию, которая изменяет основной запрос только на страницах категорий. Это делается для того, чтобы не нарушать работу других запросов на сайте.

  2. Установка нужных параметров:
    В этой функции установите параметр posts_per_page и, при необходимости, добавьте условие для исключения постов, например, представленных в виде избранных.

Пример кода для функции:

function custom_category_query_adjustments( $query ) {
    // Исключаем административные панели и любые неосновные запросы
    if ( is_admin() || ! $query->is_main_query() || ! is_category() ) {
        return;
    }

    // Устанавливаем желаемое число постов на странице
    $query->set( 'posts_per_page', 6 );

    // Исключаем избранные посты, если такие имеются
    $featured_posts_to_exclude = get_featured_posts_to_exclude();
    if ( !empty($featured_posts_to_exclude) ) {
        $query->set( 'post__not_in', $featured_posts_to_exclude );
    }
}
add_action( 'pre_get_posts', 'custom_category_query_adjustments', 1 );

Обратите внимание:

  • Контекст использования: Убедитесь, что функция get_featured_posts_to_exclude() корректно возвращает массив ID постов, которые нужно исключить. Это важно для полного контроля над выводом контента.
  • Исключение изменений на админ-панели: Использование условия is_admin() предотвращает применение изменений на страницах администрирования, что позволяет избежать нежелательного поведения в административной части WordPress.

Заключение:

Использование pre_get_posts — это оптимальный способ для настройки параметров запроса в WordPress, который позволяет более грамотно и безопасно управлять поведением вывода постов, избегая потенциальных конфликтов с другими частями системы и настройками по умолчанию. При правильной настройке это обеспечит корректную работу пагинации и вывод нужного количества постов на странице категории.

Тщательная настройка основных запросов WordPress снижает сложность и потенциальные ошибки при кастомизации, что улучшает общее пользовательское восприятие сайта.

Оцените материал
Добавить комментарий

Капча загружается...