Вопрос или проблема
В моем шаблоне категории (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('«')) {
printf('<li>%s</li>' . "\n", get_previous_posts_link('«'));
}
/** Ссылка на первую страницу, плюс многоточие при необходимости */
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('»', $max)) {
printf('<li>%s</li>' . "\n", get_next_posts_link('»', $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
:
-
Определение функции-хука:
Создайте функцию, которая изменяет основной запрос только на страницах категорий. Это делается для того, чтобы не нарушать работу других запросов на сайте. -
Установка нужных параметров:
В этой функции установите параметр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 снижает сложность и потенциальные ошибки при кастомизации, что улучшает общее пользовательское восприятие сайта.