Верх страницы
Обложка к записи Не за#буйте с вашей пагинацией!!!
Время для прочтения: 1 мин. 35 сек.

Не за#буйте с вашей пагинацией!!!

Решаем один из самых популярных вопросов у разработчиков WordPress: «Почему не работает пагинация?»

Так почему же?

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

Существуют страницы постов (постов/страниц/кастомных типов постов(CPT)) или архивные страницы(архивные/терминов страницы и страница поиска).

Пагинация на странице постов

В целом пагинацию можно разбить на 3 небольших этапа:

  • Делаем правильную выборку с помощью WP_Query;
  • Выводим пагинацию;
  • Выводим 404, когда это необходимо

WP_Query для пагинации

$current = absint(
	max(
		1,
		get_query_var( 'paged' ) ? get_query_var( 'paged' ) : get_query_var( 'page' )
	)
);
$posts_per_page = 3;
$query          = new WP_Query(
	[
		'post_type'      => 'post',
		'posts_per_page' => $posts_per_page,
		'paged'          => $current,
	]
);

Нужно обратить внимание на параметр paged в WP_Query, он говорит о том, какую страницу данного запроса вывести. С помощью функций get_query_var() мы получаем номер текущей страницы …/page/{# страницы}. В зависимости от типа страницы мы должны использовать get_query_var( 'paged' ) или get_query_var( 'page' ). Я решил написать универсальный вариант, который подойдет к любому типу страниц.

Вывод пагинации

Для вывода пагинации используем функцию paginate_links:

echo wp_kses_post(
	paginate_links(
		[
			'total'   => $query->max_num_pages,
			'current' => $current,
		]
	)
);

В функцию paginate_links нужно передать два параметра: total — количество страниц и current — текущую страницу.

Так же можно использовать the_posts_pagination, но важно помнить, что данная функция работает только с глобальным wp_query и ее вызов выглядит примерно так:

global $wp_query;
$restore_wp_query = $wp_query;
$wp_query         = $query;
the_posts_pagination();
$wp_query = $restore_wp_query;

Вывод 404 в случае, когда данных нет

if ( $query->have_posts() ) {
...
} else {
	global $wp_query;
	$wp_query->set_404();
	status_header( 404 );
	nocache_headers();
	require get_404_template();
}

Устанавливаем 404 ответ в глобальный WP_Query. Ставим 404 статус ответа для сервера. nocache_headers — позволяет сбросить кеш для заголовков ответа во всех браузерах. И подключаем 404 страницу с помощью require get_404_template().

Важно, чтобы до вывода данных ф-ций ничего не выводилось на странице!

Результат

$current = absint(
	max(
		1,
		get_query_var( 'paged' ) ? get_query_var( 'paged' ) : get_query_var( 'page' )
	)
);
$posts_per_page = 3;
$query          = new WP_Query(
	[
		'post_type'      => 'post',
		'posts_per_page' => $posts_per_page,
		'paged'          => $current,
	]
);
if ( $query->have_posts() ) {
	?>
	<h1>Текущая страница <?php echo absint( $current ); ?></h1>

	<?php
	while ( $query->have_posts() ) {
		$query->the_post();
		the_title();
		echo '<br>';
	}
	wp_reset_postdata();

	echo wp_kses_post(
		paginate_links(
			[
				'total'   => $query->max_num_pages,
				'current' => $current,
			]
		)
	);
} else {
	global $wp_query;
	$wp_query->set_404();
	status_header( 404 );
	nocache_headers();
	require get_404_template();
}

Пагинация на архивный страницах

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

Пример страницы

if ( have_posts() ) {
	while( have_posts() ) {
		the_post();
		the_title();
		echo '<br>';
	}
	wp_reset_postdata();
}

Пагинация

Для архивных страниц можно использовать функцию the_posts_pagination.

if ( have_posts() ) {
	while( have_posts() ) {
		the_post();
		the_title();
		echo '<br>';
	}
	wp_reset_postdata();
	the_posts_pagination();
}

Важно, что данная функция работает только с глобальным wp_query.

Изменяем результат запроса с помощью pre_get_posts

Добавляем в functions.php:

add_action( 'pre_get_posts', 'customize_search_query' );
function customize_search_query( WP_Query $wp_query ) {
	if ( $wp_query->is_main_query() && ! $wp_query->is_admin() && $wp_query->is_search() ) {
		$wp_query->set( 'cat', 10 );
	}
}

С данным хуком нужно быть осторожным. Для начала нужно проверить, что данный запрос является основным с помощью метода is_main_query. Так же проверить, что данных запрос вызывается на фронте с помощью is_admin и то, что данный запрос является поиском is_search. Затем устанавливаем конкретную категорию set( 'cat', 10 );

Источник: WP Punk.

Автор: Кобзарёв Михаил

Русский разработчик со стажем. Работаю с PHP, ООП, JavaScript, Git, WordPress, Joomla, Drupal, Opencart, Laravel, Moonshine.

Оптимизирую сайты под Google Page Speed, настраиваю импорты для больших магазинов на WooCommerce + WP All Import. Пишу плагины на заказ. Все мои услуги.

Веду блог о разработке, дайджест в телеграмме и в ВК.

Вы всегда можете нанять меня.

Комментарии
Подписаться
Уведомить о
guest

3 комментариев
Межтекстовые Отзывы
Посмотреть все комментарии
Andry
Andry
1 год назад

В процессе тестирования вашего кода на странице категорий, хотел вывеси посты с пагинацией этой категории. Создал 8 постов, применил указанный код из Результата. 1 и 2 страницы нормально посты отобразили (/category/uncategorized/page/2/), а вот нажимая на 3-ю ссылку редиректит на 404. Можете ли, как-то перепроверить код ? А то не работает( WP v 6.0.1

Последний раз редактировалось 1 год назад Andry ем
Andry
Andry
1 год назад
Ответить на  Кобзарёв Михаил

Проверил, не работает. На самом деле, у меня немного иная задача. Мне необходимо сделать пагинацию для страницы на которой выводятся произвольные типы записи.
Вот так добавил тип записи в functions.php

add_action('init', function () {

    $labels = array(
        'name' => 'News',
        'singular_name' => 'News',
        'add_new' => 'Add News',
        'add_new_item' => 'Add News',
        'edit_item' => 'Corrected news',
        'new_item' => 'New news',
        'all_items' => 'All news',
        'search_items' => 'Search news',
        'not_found' => 'News in these types were not found',
        'not_found_in_trash' => 'No such news were found in the basket',
        'menu_name' => 'News'
    );

    $args = array(
        'labels' => $labels,
        'public' => true,
        'publicly_queryable' => false,
        'has_archive' => true,
        'menu_icon' => 'dashicons-email-alt2',
        'menu_position' => 5,
        'supports' => array('title', 'editor', 'custom-fields', 'thumbnail'),
        'taxonomies' => array('category', 'post_tag'),
        'rewrite' => true,
        'query_var' => true,

    );

    register_post_type('news', $args);
});

Потом создал как описано в статье пагинацию на странице page-news.php

 <div id="response" class="block_list">
    <?php
    $current = absint(
        max(
            1,
            get_query_var( 'paged' ) ? get_query_var( 'paged' ) : get_query_var( 'page' )
        )
    );
    $query = new WP_Query(
        [
            'post_type' => 'news',
            'posts_per_page' => '5',
            'paged' => $current,
        ]
    );

    if ($query->have_posts()) {
        while ($query->have_posts()) : $query->the_post();
            $title = get_the_title();

            $publication_arr = get_post_custom_values('publication_img');

            if (is_null($publication_arr) || $publication_arr[0] == '0') {
                $publication_img_src = get_template_directory_uri() . '/images/no-image.jpg';
            } else {
                $publication_img_src = '';
            }

            echo '<div class="block_item">
                    <img class="block_item__img" src="' . $publication_img_src . '" alt="' . $title . '">
                    <a class="block_item__link" href="' . get_permalink() . '">' . $title . '</a>
              </div>';
        endwhile; ?>

        <div class="category_pagination">
        <?php

        echo paginate_links( array(
            'base' => get_pagenum_link(1) . '%_%',
            'format' => 'page/%#%/',
            'total' => $query->max_num_pages,
            'current' => $current,
            'show_all' => false,
            'end_size' => 1,
            'mid_size' => 2,
            'prev_next' => true,
            'prev_text' => __('Previous', 'test'),
            'next_text' => __('Next', 'test'),
            'class' => '',
        ) );
        ?>
        </div>

    <?php }
    wp_reset_postdata(); ?>

<?php else : ?><p><?php esc_html_e('Missing posts with defined criteria', 'test'); ?></p><?php endif; ?>
    </div>

Только расширил значения для функции paginate_links. Так вот ссылки пагинации отображает, а вот при нажатии на них редиректит на index.php Может есть совет, как решить эту задачу для произвольных типов записи?

Предыдущая запись

Давайте дружить
в Телеграме

Авторский блог вашего покорного слуги в Telegram про web, программирование, алгоритмы, инструменты разработчика, WordPress, Joomla, Opencart, Laravel, Moonshine, фильмы и сериалы