Вопрос или проблема
Я пытаюсь изменить стандартный запрос WP, чтобы избежать проблем с пагинацией.
Необходимо получить точное количество постов независимо от максимального количества постов на страницу.
Я хочу иметь возможность выбирать, где и когда показывать только 1, 2 или любое количество постов. Поэтому, если я выберу число больше максимального количества постов на страницу, категория будет разбита на страницы. Таким образом, оба параметра важны: ‘numberposts’ и ‘posts_per_page’.
Но стандартный запрос WP, вызванный через query_posts(), не использует параметр ‘numberposts’ и полностью его игнорирует. И если я установлю параметр ‘posts_per_page’ (вместо ‘numberposts’) на, например, 1, категория просто разделится на множество страниц, каждая с одним постом.
Так как же это достичь? Если это вообще возможно со стандартным запросом…
$category = get_queried_object();
$category_metadata = get_fields('category_'. $category->term_id);
$posts_output = isset($category_metadata['ctgr_posts_out']) ? $category_metadata['ctgr_posts_out'] : false;
$posts_order = isset($category_metadata['ctgr_posts_order']) ? $category_metadata['ctgr_posts_order'] : 'datetolow';
$posts_mode = isset($category_metadata['ctgr_posts_mode']) ? $category_metadata['ctgr_posts_mode'] : 'simple';
$posts_total = isset($category_metadata['ctgr_posts_total']) ? $category_metadata['ctgr_posts_total'] : -1; // << ЭТО ПРОБЛЕМА, значение, переданное из метаполя, не влияет
$posts_per_page = isset($category_metadata['ctgr_posts_per_page']) ? $category_metadata['ctgr_posts_per_page'] : 20; // << ЭТО ТОЖЕ ВАЖНО и не должно быть удалено
$posts_date_format = isset($category_metadata['ctgr_posts_date_format']) ? $category_metadata['ctgr_posts_date_format'] : null;
$posts_body = null;
$pagination_body = null;
$query_custom_args = [
'post_status' => 'publish',
'numberposts' => $posts_total,
'posts_per_page' => $posts_per_page,
];
switch($posts_output) {
case 'own':
// только собственные посты
$query_custom_args['category__in'] = $category->term_id;
break;
case 'all':
// собственные и подкатегорийные посты
$query_custom_args['category'] = $category->term_id;
break;
default:
// значение 'hide'
$posts_output = false;
}
switch($posts_order) {
case 'datetolow':
$query_custom_args['orderby'] = 'date';
$query_custom_args['order'] = 'DESC';
break;
case 'datetohigh':
$query_custom_args['orderby'] = 'date';
$query_custom_args['order'] = 'ASC';
break;
default:
// значение 'titletohigh'
$query_custom_args['orderby'] = 'title';
$query_custom_args['order'] = 'ASC';
}
global $query_string;
parse_str($query_string, $query_default_args);
$query_args = array_merge($query_default_args, $query_custom_args);
query_posts($query_args);
if($posts_output && have_posts()) {
// посты
$posts_body = '<div class="category_posts_'. $posts_mode .' category_posts"><ul class="posts_list">';
while(have_posts()) {
the_post();
$post_alt_title = get_field('post_alt_title', $post);
$post_title = $post_alt_title ? string_unbreak($post_alt_title) : get_the_title($post);
$post_link = get_permalink($post);
$post_image_url = $post_excerpt = $post_date = null;
$post_image_body = $post_date_body = null;
if($posts_mode != 'simple') {
$post_image_url = get_the_post_thumbnail_url($post, 'large') ?: null;
$post_alt_excerpt = get_field('post_alt_excerpt', $post);
$post_excerpt = $post_alt_excerpt ?: get_the_excerpt($post);
$post_date = $posts_date_format ? get_the_date($posts_date_format, $post) : null;
$post_image_body = $post_image_url ? '<img class="post_image" src="'. $post_image_url .'" alt="'. $post_title .'">' : null;
$post_date_body = $post_date ? '<div class="post_date">'. $post_date .'</div>' : null;
}
if($posts_mode == 'extended') {
$posts_body .= '
<li class="post_extended post_item">
'. $post_image_body .'
<div class="post_description">
<div class="post_title">
<h2><a href="'. $post_link .'">'. $post_title .'</a></h2>
</div>
<div class="post_text text_content">'. $post_excerpt .'</div>
<div class="post_extra">
'. $post_date_body .'
<a class="post_more" href="'. $post_link .'">Читать далее >>></a>
</div>
</div>
</li>';
}
else { // значение 'simple'
$posts_body .= '
<li class="post_simple post_item">
<a class="post_link" href="'. $post_link .'">'. $post_title .'</a>
</li>';
}
}
$posts_body .= '</ul></div>';
// пагинация
if($wp_query->max_num_pages > 1) {
$pagination_body = '
<div class="category_pagination">'.
get_the_posts_pagination([
'show_all' => false,
'end_size' => 3,
'mid_size' => 5,
'prev_next' => false,
'prev_text' => __('Назад'),
'next_text' => __('Вперед'),
'add_args' => false,
'add_fragment' => '',
'screen_reader_text' => __( 'Навигация по постам' ),
])
.'</div>';
}
}
wp_reset_query();
.
Ответ или решение
Чтобы точно определить количество постов в стандартном запросе WordPress, нужно понимать, какие параметры влияют на формирование результата и как они взаимодействуют. Вопрос задает внимание на конфликте между параметрами numberposts
и posts_per_page
, и требует подхода, который учитывает пагинацию и гибкость настройки числа постов на странице.
Теория
WordPress предлагает несколько способов запроса постов, и двое из них — query_posts()
и get_posts()
— часто путаются пользователями. Когда вы используете query_posts()
, вы модифицируете главный цикл, управляемый $wp_query
, что может вызывать проблемы с пагинацией, как в вашем случае.
Параметр numberposts
на самом деле используется в функции get_posts()
, но не поддерживается в query_posts()
и WP_Query. Вместо него обычно используется posts_per_page
, который контролирует число постов, которые отображаются на странице.
Для корректного управления показом постов важно понять, что комбинация параметров posts_per_page
и paged
(номер текущей страницы) как раз и управляет тем, какие и сколько постов выводятся на данной странице.
Пример
Ваша проблема может быть решена путем корректного использования WP_Query вместо query_posts, дадим вам полноценный пример:
- Подсчитайте общее количество постов в категории:
$category_id = get_queried_object_id();
$total_posts = new WP_Query([
'cat' => $category_id,
'posts_per_page' => -1,
'fields' => 'ids'
]);
$total_post_count = $total_posts->post_count;
Эта часть кода даст вам точное количество постов в категории. Параметр 'posts_per_page' => -1
извлекает все посты в категории без ограничения по числу.
- Теперь используем WP_Query для пагинации и отображения постов:
$paged = (get_query_var('paged')) ? get_query_var('paged') : 1;
$args = [
'cat' => $category_id,
'posts_per_page' => $posts_per_page, // количество постов на одной странице
'paged' => $paged, // текущая страница
'order' => 'DESC',
'orderby' => 'date'
];
$custom_query = new WP_Query($args);
if ($custom_query->have_posts()) :
while ($custom_query->have_posts()) : $custom_query->the_post();
// Ваш вывод поста
endwhile;
// Пагинация
echo paginate_links([
'total' => $custom_query->max_num_pages
]);
endif;
wp_reset_postdata();
В этом примере мы используем параметр paged
для обработки пагинации, что позволяет добиваться гибкости в отображении постов на различных страницах.
Применение
Теперь, когда вы понимаете, как использовать WP_Query и параметры, влияющие на количество постов, вы можете динамически настроить ваш код в зависимости от ваших потребностей. Если вы хотите выводить больше постов на странице, контролируйте это изменением значения переменной $posts_per_page
. Убедитесь, что функция paginate_links()
правильно отображает и навигацию между страницами.
Таким образом, главным партнером в решении проблемы с точным подсчетом и отображением постов является комбинация использования WP_Query с правильно установленными параметрами posts_per_page
и paged
, что позволит вам управлять не только выводом, но и общей логикой навигации по категориям.
Этот подход обеспечит не просто корректную работу сайта в широком диапазоне сценариев, но и даст гибкость в дальнейшем развитии функциональности, снижая риск потенциальных конфликтов или ошибок, связанных с пагинацией.