Вопрос или проблема
Я пытаюсь манипулировать стандартным 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();
.
Ответ или решение
Вопрос, который вы рассматриваете, касается манипуляции с дефолтным WP-запросом в WordPress, чтобы можно было управлять количеством выводимых постов независимо от настроек пагинации. Для достижения этого эффекта необходимо использовать правильные параметры и функции, которые предоставляются WordPress. Разберём теорию, дадим конкретный пример и покажем, как применить это в вашем коде.
Теория
В WordPress дефолтные параметры запроса сильно зависят от используемых функций и методов. Функция query_posts()
не поддерживает параметр 'numberposts'
, так как это метод, более свойственный функции get_posts()
. В то же время, query_posts()
и WP_Query
используют параметр 'posts_per_page'
, который позволяет указать число постов на страницу, что не всегда помогает в случаях вашей задачи, поскольку это влияет на пагинацию.
Для более точного контроля над запросами, рекомендуется использовать WP_Query
вместо query_posts()
, так как это предлагает более гибкие возможности настройки и не нарушает глобальный wp_query, как это делает query_posts()
.
Пример
Чтобы получить точное количество постов без вмешательства в пагинацию, можно использовать следующий подход:
$category = get_queried_object();
$category_metadata = get_fields('category_' . $category->term_id);
$posts_total = isset($category_metadata['ctgr_posts_total']) ? (int)$category_metadata['ctgr_posts_total'] : -1;
$posts_per_page = isset($category_metadata['ctgr_posts_per_page']) ? (int)$category_metadata['ctgr_posts_per_page'] : 20;
$args = [
'post_status' => 'publish',
'posts_per_page' => $posts_total, // Используем 'posts_per_page' для общего количества постов
'cat' => $category->term_id, // Заменяем 'category' и 'category__in' на 'cat' для более точного запроса
'orderby' => 'date',
'order' => 'DESC',
];
$query = new WP_Query($args);
Таким образом, вы можете установить 'posts_per_page'
равным количеству, необходимому для вашей задачи. Если вы хотите отображать все посты в категории без пагинации, можете установить значение 'posts_per_page'
в -1
.
Применение в вашем коде
На основе предоставленного кода, вы можете заменить query_posts()
следующим фрагментом, чтобы использовать WP_Query
:
global $wp_query;
$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']) ? (int)$category_metadata['ctgr_posts_total'] : -1;
$posts_per_page = isset($category_metadata['ctgr_posts_per_page']) ? (int)$category_metadata['ctgr_posts_per_page'] : 20;
$posts_date_format = isset($category_metadata['ctgr_posts_date_format']) ? $category_metadata['ctgr_posts_date_format'] : null;
$args = [
'post_status' => 'publish',
'posts_per_page' => $posts_total, // Теперь количество постов контролируется этим параметром
'cat' => $category->term_id,
'orderby' => $posts_order == 'datetolow' ? 'date' : ($posts_order == 'datetohigh' ? 'date' : 'title'),
'order' => $posts_order == 'datetolow' ? 'DESC' : 'ASC',
];
$wp_query = new WP_Query($args);
if ($posts_output && $wp_query->have_posts()) {
$posts_body = '<div class="category_posts_' . $posts_mode . ' category_posts"><ul class="posts_list">';
while ($wp_query->have_posts()) {
$wp_query->the_post();
// Остальной код остается таким же
}
// Завершение цикла и отображение постов
}
wp_reset_postdata();
Заключение
Этот подход, предложенный с использованием WP_Query
, позволяет вам более гибко управлять количеством выводимых постов без нарушения стандартной пагинации WordPress. Используйте posts_per_page
для установки точного числа необходимых постов. Помните также про wp_reset_postdata()
, чтобы вовремя сбросить данные после пользовательского запроса и не нарушить работу глобального объекта $wp_query
.