WordPress как на ладони
Очень Удобный и Быстрый Хостинг для сайтов на WordPress. Пользуюсь сам и вам рекомендую!

paginate_links()WP 2.1.0

Позволяет создать ссылки пагинации для любых страниц.

Пример того как выглядит планация: « предыдущая 1 … 3 4 5 6 7 … 9 следующая »

Технически функцию можно использовать для создания пагинации где угодно. Эта функция является ядром для всех функций пагинации в WordPress.

Для построения пагинации в WordPress есть также специальные функции (обертки для этой функции):

Эта функция жестко добавляет все $_GET параметры текущего запроса (текущей страницы) в ссылки пагинации. Поэтому, например, если она используется в обработчике AJAX запроса, то все URL в ней будут содержать текущие GET параметры. Тикет по этой теме и обсуждение ниже в комментариях.

Немного о параметрах

Параметр total должен получить общее количество страниц пагинации, а параметр current номер текущей страницы пагинации.

Пример параметра base - http://example.com/all_posts.php%_%, где %_% будет заменено тем что указано в параметре format = '?page=%#%', здесь %#% будет заменено числом текущей страницы пагинации.

Вообще, в base можно сразу указать например так: http://example.com/all_posts.php?page=%#%, и при этом format = '' (пусто).

В ссылки можно добавить переменные запроса, для этого укажите нужные переменные и их значения в виде массива в параметр add_args. Подробнее см. функцию add_query_arg().

Чтобы добавить ссылки на предыдущую/следующую страницу, нужно включить логический параметр prev_next (указать ему true), а затем можно установить текст ссылок указав параметры prev_text / next_text (предыдущая ссылка/следующая ссылка).

Хуки из функции

Возвращает

Строку|Строку[]|null.

Шаблон использования

$args = [
	'base'         => '%_%',
	'format'       => '?page=%#%',
	'total'        => 1,
	'current'      => 0,
	'show_all'     => False,
	'end_size'     => 1,
	'mid_size'     => 2,
	'prev_next'    => True,
	'prev_text'    => __('« Previous'),
	'next_text'    => __('Next »'),
	'type'         => 'plain',
	'add_args'     => False,
	'add_fragment' => '',
	'before_page_number' => '',
	'after_page_number'  => ''
];

echo paginate_links( $args );

Использование

paginate_links( $args );
$args(строка/массив)
Аргументы для построения пагинации.
По умолчанию: предустановки

Аргументы параметра $args

base(строка)

Базовый формат УРЛ, который будет использован для создания ссылки пагинации. Например: http://example.com/all_posts.php%_% здесь %_% будет заменено значением аргумента format.

В base можно сразу указать например так: http://example.com/all_posts.php?page=%#%, и при этом указать пустую строку в format.

По умолчанию: '%_%'

format(строка)
Формат замены.
По умолчанию: '?page=%#%'
total(число)
Общее количество страниц, которые участвуют в пагинации.
По умолчанию: 1
current(число)
Номер текущей страницы пагинации.
show_all(логический)
true - будут показаны все страницы участвующие в пагинации.
false - (По умолчанию) показывается только несколько ссылок спереди и сзади номера текущей страницы. Количество ссылок регулируется параметрами end_size и mid_size.
По умолчанию: false
end_size(число)
Сколько ссылок показать с начала и конца:
"предыдущая 1 2 ... [4] ... 8 9 следующая".
По умолчанию: 1
mid_size(число)
Сколько номеров показывать до и после текущего номера:
1 ... 2 3 [4] 5 6 ... 99.
По умолчанию: 2
prev_next(логический)
Выводить боковые ссылки "предыдущая/следующая страница". По умолчанию выводятся, если НЕ нужно выводить эти ссылки пишем false.
По умолчанию: true
prev_text(строка)
Текст ссылки "предыдущая страница".
По умолчанию: __('« Previous')
next_text(строка)
Текст ссылки "следующая страница".
По умолчанию: __('Next »')
type(строка)

Контролирует в каком формате будет возвращен результат:

  • plain- просто ссылки разделенные пробелом (По умолчанию).
  • array - в виде массива данных для дальнейшей обработки в PHP.
  • list - <ul> список.

По умолчанию: 'plain'

add_args(массив)
Массив переменных запроса, которые нужно добавить к ссылкам пагинации. См. функцию add_query_arg().
По умолчанию: array()
add_fragment(строка)
Текст который добавиться ко всем ссылкам.
По умолчанию: ''
aria_current(строка) (WP 4.9)
Значение атрибута aria-current. Возможные значения: 'page', 'step', 'location', 'date', 'time', 'true', 'false'.
По умолчанию: 'page'.
before_page_number(строка)
Текст/строка перед числом пагинации.
after_page_number(строка)

Текст/строка после числа пагинации.

Параметры before_page_number и after_page_number позволяют обернуть само число пагинации. Например в тег <span> для стилизации.

Вообще, эти параметры были созданы для того, чтобы указать текст для роботов, чтобы при просмотре кода, было понятно для чего предназначены ссылки.

Примеры

5

#1 Пагинация, аналог wp_pagenavi

Чтобы добавить пагинацию на страницу результатов поиска или страницу архивов, используйте такой код:

function my_pagenavi() {
	global $wp_query;

	$big = 999999999; // уникальное число для замены

	$args = array(
		'base'    => str_replace( $big, '%#%', get_pagenum_link( $big ) ),
		'format'  => '',
		'current' => max( 1, get_query_var('paged') ),
		'total'   => $wp_query->max_num_pages,
	);

	$result = paginate_links( $args );

	// удаляем добавку к пагинации для первой страницы
	$result = preg_replace( '~/page/1/?([\'"])~', '', $result );

	echo $result;
}

// Теперь, где нужно вывести пагинацию используем
// my_pagenavi();
3

#2 Пагинация для произвольного запроса WP_Query

В примере ниже будем выводить продукты WooCommerce (post_type=product) на отдельной странице записи (post_type=post). Т.е. будем использовать произвольный запрос и сделаем для него пагинацию.

// Запрашиваем продукты
$query = new WP_Query( [
	'post_type'      => 'product',
	'posts_per_page' => 5,
	'paged'          => get_query_var( 'page' ),
] );

// Обрабатываем полученные в запросе продукты, если они есть
if ( $query->have_posts() ) {

	while ( $query->have_posts() ) {
		$query->the_post();

		// выводим заголовок
		the_title();
	}

	wp_reset_postdata();
}

// Выводим пагинацию, если продуктов больше запрошенного количество
echo paginate_links( [
	'base'    => user_trailingslashit( wp_normalize_path( get_permalink() .'/%#%/' ) ),
	'current' => max( 1, get_query_var( 'page' ) ),
	'total'   => $query->max_num_pages,
] );

Мы указали выводить по 5 товаров, если их, к примеру 22, то будет выведено 5 элементов пагинации, из которых 4 будут ссылками следующего вида:

Текущая страница, ссылка не выводится (5 продуктов)
http://example.com/название-записи/2/ (5 продуктов)
http://example.com/название-записи/3/ (5 продуктов)
http://example.com/название-записи/4/ (5 продуктов)
http://example.com/название-записи/5/ (2 продукта)

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

  • В параметре base где формируется вид ссылки пагинации не используются такие слова как page или paged. Потому что c page будет перенаправление со страницы пагинации на саму запись, а с paged будет 404 ошибка на странице пагинации.

  • Для получения номера страницы пагинации вместо привычного get_query_var( 'paged' ) используем get_query_var( 'page' ).

  • Этот код не будет работать на страницах у которых контент делиться на несколько страниц тегом <!--nextpage-->, подробнее см. здесь.

3

#3 Альтернатива этой фукнции

paginate_links() всегда возвращает HTML, даже если параметр type=array вы получите массив готовых <a> тегов. Это может не подойти когда нужно полностью изменить HTML-структуру вашей пагинации. Ниже небольшая функция, которая возвращает массив объектов вместо HTML.

/**
 * Generates array of pagination links.
 *
 * @author Kama (wp-kama.com)
 * @varsion 2.5
 *
 * @param array $args {
 *
 *     @type int    $total        Maximum allowable pagination page.
 *     @type int    $current      Current page number.
 *     @type string $url_base     URL pattern. Use `{pagenum}` placeholder.
 *     @type string $first_url    URL to first page. Default: '' - taken automaticcaly from $url_base.
 *     @type int    $mid_size     Number of links before/after current: 1 ... 1 2 [3] 4 5 ... 99. Default: 2.
 *     @type int    $end_size     Number of links at the edges: 1 2 ... 3 4 [5] 6 7 ... 98 99. Default: 1.
 *     @type bool   $show_all     true - Show all links. Default: false.
 *     @type string $a_text_patt  `%s` will be replaced with number of pagination page. Default: `'%s'`.
 *     @type bool   $is_prev_next Whether to show prev/next links. « Previou 1 2 [3] 4 ... 99 Next ». Default: false.
 *     @type string $prev_text    Default: `« Previous`.
 *     @type string $next_text    Default: `Next »`.
 * }
 *
 * @return array
 */
function kama_paginate_links_data( array $args ): array {
	global $wp_query;

	$args += [
		'total'        => 1,
		'current'      => 0,
		'url_base'     => '/{pagenum}',
		'first_url'    => '',
		'mid_size'     => 2,
		'end_size'     => 1,
		'show_all'     => false,
		'a_text_patt'  => '%s',
		'is_prev_next' => false,
		'prev_text'    => '« Previous',
		'next_text'    => 'Next »',
	];

	$rg = (object) $args;

	$total_pages = max( 1, (int) ( $rg->total ?: $wp_query->max_num_pages ) );

	if( $total_pages === 1 ){
		return [];
	}

	// fix working parameters

	$rg->total = $total_pages;
	$rg->current = max( 1, abs( $rg->current ?: get_query_var( 'paged', 1 ) ) );

	$rg->url_base = $rg->url_base ?: str_replace( PHP_INT_MAX, '{pagenum}', get_pagenum_link( PHP_INT_MAX ) );
	$rg->url_base = wp_normalize_path( $rg->url_base );

	if( ! $rg->first_url ){
		// /foo/page(d)/2 >>> /foo/ /foo?page(d)=2 >>> /foo/
		$rg->first_url = preg_replace( '~/paged?/{pagenum}/?|[?]paged?={pagenum}|/{pagenum}/?~', '', $rg->url_base );
		$rg->first_url = user_trailingslashit( $rg->first_url );
	}

	// core array

	if( $rg->show_all ){
		$active_nums = range( 1, $rg->total );
	}
	else {

		if( $rg->end_size > 1 ){
			$start_nums = range( 1, $rg->end_size );
			$end_nums = range( $rg->total - ($rg->end_size - 1), $rg->total );
		}
		else {
			$start_nums = [ 1 ];
			$end_nums = [ $rg->total ];
		}

		$from = $rg->current - $rg->mid_size;
		$to = $rg->current + $rg->mid_size;

		if( $from < 1 ){
			$to = min( $rg->total, $to + absint( $from ) );
			$from = 1;

		}
		if( $to > $rg->total ){
			$from = max( 1, $from - ($to - $rg->total) );
			$to = $rg->total;
		}

		$active_nums = array_merge( $start_nums, range( $from, $to ), $end_nums );
		$active_nums = array_unique( $active_nums );
		$active_nums = array_values( $active_nums ); // reset keys
	}

	// fill by core array

	$pages = [];

	if( 1 === count( $active_nums ) ){
		return $pages;
	}

	$item_data = static function( $num ) use ( $rg ){

		$data = [
			'is_current'   => false,
			'page_num'     => null,
			'url'          => null,
			'link_text'    => null,
			'is_prev_next' => false,
			'is_dots'      => false,
		];

		if( 'dots' === $num ){

			return (object) ( [
			   'is_dots' => true,
			   'link_text' => '…',
			] + $data );
		}

		$is_prev = 'prev' === $num && ( $num = max( 1, $rg->current - 1 ) );
		$is_next = 'next' === $num && ( $num = min( $rg->total, $rg->current + 1 ) );

		$data = [
			'is_current'   => ! ( $is_prev || $is_next ) && $num === $rg->current,
			'page_num'     => $num,
			'url'          => 1 === $num ? $rg->first_url : str_replace( '{pagenum}', $num, $rg->url_base ),
			'is_prev_next' => $is_prev || $is_next,
		] + $data;

		if( $is_prev ){
			$data['link_text'] = $rg->prev_text;
		}
		elseif( $is_next ) {
			$data['link_text'] = $rg->next_text;
		}
		else {
			$data['link_text'] = sprintf( $rg->a_text_patt, $num );
		}

		return (object) $data;
	};

	foreach( $active_nums as $indx => $num ){

		$pages[] = $item_data( $num );

		// set dots
		$next = $active_nums[ $indx + 1 ] ?? null;
		if( $next && ($num + 1) !== $next ){
			$pages[] = $item_data( 'dots' );
		}
	}

	if( $rg->is_prev_next ){
		$rg->current !== 1 && array_unshift( $pages, $item_data( 'prev' ) );
		$rg->current !== $rg->total && $pages[] = $item_data( 'next' );
	}

	return $pages;
}

Что выводит функция:

$links_data = kama_paginate_links_data( [
	'total'    => 3,
	'current'  => 2,
	'url_base' => 'http://site.com/page-name/paged/{pagenum}',
	'mid_size' => 2,
] );

print_r( $links_data );

/*
Array
(
	[0] => stdClass Object
		(
			[is_current] =>
			[page_num] => 288
			[url] => http://site.com/page-name/paged/288
			[is_prev_next] => 1
			[link_text] => « Previous
			[is_dots] =>
		)

	[1] => stdClass Object
		(
			[is_current] =>
			[page_num] => 1
			[url] => http://site.com/page-name/
			[is_prev_next] =>
			[link_text] => 1
			[is_dots] =>
		)

	[2] => stdClass Object
		(
			[is_dots] => 1
			[link_text] => …
			[is_current] =>
			[page_num] =>
			[url] =>
			[is_prev_next] =>
		)

	[3] => stdClass Object
		(
			[is_current] =>
			[page_num] => 285
			[url] => http://site.com/page-name/paged/285
			[is_prev_next] =>
			[link_text] => 285
			[is_dots] =>
		)

	[4] => stdClass Object
		(
			[is_current] =>
			[page_num] => 286
			[url] => http://site.com/page-name/paged/286
			[is_prev_next] =>
			[link_text] => 286
			[is_dots] =>
		)

	[5] => stdClass Object
		(
			[is_current] => 1
			[page_num] => 287
			[url] => http://site.com/page-name/paged/287
			[is_prev_next] =>
			[link_text] => 287
			[is_dots] =>
		)

)
*/

Теперь используем эту функцию в цикле:

<?php

$links_data = kama_paginate_links_data( [
	'total' => 3,
	'current' => 2,
	'url_base' => 'http://site.com/page-name/paged/{pagenum}',
] );

if( $links_data ){
	?>

	<ul>
		<?php foreach( $links_data as $link ) { ?>
		<li>
			<?php if ( $link->is_dots ) { ?>
				<span><?= $link->link_text ?></span>
			<?php } elseif ( $link->is_current ) { ?>
				<strong><?= $link->link_text ?></strong>
			<?php } else { ?>
				<a href="<?php esc_attr_e( $link->url ) ?>"><?php _e( $link->link_text ) ?></a>
			<?php } ?>
		</li>
		<?php } ?>
	</ul>

	<?php
}

Получим:

<ul>
	<li>
		<a href="http://site.com/page-name/paged/1">1</a>
	</li>
	<li>
		<strong>2</strong>
	</li>
	<li>
		<a href="http://site.com/page-name/paged/3">3</a>
	</li>
</ul>

1

#4 Пример с произвольным запросом WP_Query

Когда записи выводятся отдельным запросом с помощью new WP_Query для вывода пагинации можно установить параметр total, в котором указать свойство WP_Query::$max_num_pages. Рассмотрим пример:

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

<?php
// 1 значение по умолчанию
$paged = get_query_var( 'paged' ) ? absint( get_query_var( 'paged' ) ) : 1;

$the_query = new WP_Query( array(
	'posts_per_page' => 5,
	'category_name'  => 'gallery',
	'paged'          => $paged,
) );

// цикл вывода полученных записей
while( $the_query->have_posts() ){
	$the_query->the_post();
	?>
	<!-- HTML каждой записи -->
	<?php
}
wp_reset_postdata();

// пагинация для произвольного запроса
$big = 999999999; // уникальное число

echo paginate_links( array(
	'base'    => str_replace( $big, '%#%', esc_url( get_pagenum_link( $big ) ) ),
	'current' => max( 1, get_query_var('paged') ),
	'total'   => $the_query->max_num_pages
) );
?>
0

#5 Добавим префикс для всех классов под БЭМ

Допустим мы изменили шаблон навигации через хук navigation_markup_template и теперь мы ходим чтобы все вложенные элементы соответствовали БЭМ классам.

Класс БЭМ блока у нас hlpagination:

<?php
/// изменим шаблон навигации (пагинации)
add_filter( 'navigation_markup_template', 'hl_navigation_template', 10, 2 );

function hl_navigation_template( $template, $class ){

	ob_start();
	?>
	<nav class="hlpagination %1$s" role="navigation">
		<div class="hlpagination__nav-links">%3$s</div>
	</nav>
	<?php
	return ob_get_clean();
}

%3$s заменяется на то что возвращает paginate_links() и у нее всем классам также нужно добавить префикс:

/// поправим html пагинации для функции paginate_links()
add_filter( 'paginate_links_output', 'hl_fix_paginate_links' );

function hl_fix_paginate_links( $html ){

	$html = preg_replace_callback( '/ class=[\'"][^\'"]+[\'"]/', static function( $mm ){

		return strtr( $mm[0], [
			'current'      => '--current',
			'prev'         => 'hlpagination__prev',
			'next'         => 'hlpagination__next',
			'dots'         => 'hlpagination__dots',
			'page-numbers' => 'hlpagination__numbers',
		] );

	}, $html );

	return $html;
}

Заметки

  • Global. WP_Query. $wp_query WordPress Query object.
  • Global. WP_Rewrite. $wp_rewrite WordPress rewrite component.

Список изменений

С версии 2.1.0 Введена.
С версии 4.9.0 Added the aria_current argument.

Код paginate_links() WP 6.5.2

function paginate_links( $args = '' ) {
	global $wp_query, $wp_rewrite;

	// Setting up default values based on the current URL.
	$pagenum_link = html_entity_decode( get_pagenum_link() );
	$url_parts    = explode( '?', $pagenum_link );

	// Get max pages and current page out of the current query, if available.
	$total   = isset( $wp_query->max_num_pages ) ? $wp_query->max_num_pages : 1;
	$current = get_query_var( 'paged' ) ? (int) get_query_var( 'paged' ) : 1;

	// Append the format placeholder to the base URL.
	$pagenum_link = trailingslashit( $url_parts[0] ) . '%_%';

	// URL base depends on permalink settings.
	$format  = $wp_rewrite->using_index_permalinks() && ! strpos( $pagenum_link, 'index.php' ) ? 'index.php/' : '';
	$format .= $wp_rewrite->using_permalinks() ? user_trailingslashit( $wp_rewrite->pagination_base . '/%#%', 'paged' ) : '?paged=%#%';

	$defaults = array(
		'base'               => $pagenum_link, // http://example.com/all_posts.php%_% : %_% is replaced by format (below).
		'format'             => $format, // ?page=%#% : %#% is replaced by the page number.
		'total'              => $total,
		'current'            => $current,
		'aria_current'       => 'page',
		'show_all'           => false,
		'prev_next'          => true,
		'prev_text'          => __( '&laquo; Previous' ),
		'next_text'          => __( 'Next &raquo;' ),
		'end_size'           => 1,
		'mid_size'           => 2,
		'type'               => 'plain',
		'add_args'           => array(), // Array of query args to add.
		'add_fragment'       => '',
		'before_page_number' => '',
		'after_page_number'  => '',
	);

	$args = wp_parse_args( $args, $defaults );

	if ( ! is_array( $args['add_args'] ) ) {
		$args['add_args'] = array();
	}

	// Merge additional query vars found in the original URL into 'add_args' array.
	if ( isset( $url_parts[1] ) ) {
		// Find the format argument.
		$format       = explode( '?', str_replace( '%_%', $args['format'], $args['base'] ) );
		$format_query = isset( $format[1] ) ? $format[1] : '';
		wp_parse_str( $format_query, $format_args );

		// Find the query args of the requested URL.
		wp_parse_str( $url_parts[1], $url_query_args );

		// Remove the format argument from the array of query arguments, to avoid overwriting custom format.
		foreach ( $format_args as $format_arg => $format_arg_value ) {
			unset( $url_query_args[ $format_arg ] );
		}

		$args['add_args'] = array_merge( $args['add_args'], urlencode_deep( $url_query_args ) );
	}

	// Who knows what else people pass in $args.
	$total = (int) $args['total'];
	if ( $total < 2 ) {
		return;
	}
	$current  = (int) $args['current'];
	$end_size = (int) $args['end_size']; // Out of bounds? Make it the default.
	if ( $end_size < 1 ) {
		$end_size = 1;
	}
	$mid_size = (int) $args['mid_size'];
	if ( $mid_size < 0 ) {
		$mid_size = 2;
	}

	$add_args   = $args['add_args'];
	$r          = '';
	$page_links = array();
	$dots       = false;

	if ( $args['prev_next'] && $current && 1 < $current ) :
		$link = str_replace( '%_%', 2 == $current ? '' : $args['format'], $args['base'] );
		$link = str_replace( '%#%', $current - 1, $link );
		if ( $add_args ) {
			$link = add_query_arg( $add_args, $link );
		}
		$link .= $args['add_fragment'];

		$page_links[] = sprintf(
			'<a class="prev page-numbers" href="%s">%s</a>',
			/**
			 * Filters the paginated links for the given archive pages.
			 *
			 * @since 3.0.0
			 *
			 * @param string $link The paginated link URL.
			 */
			esc_url( apply_filters( 'paginate_links', $link ) ),
			$args['prev_text']
		);
	endif;

	for ( $n = 1; $n <= $total; $n++ ) :
		if ( $n == $current ) :
			$page_links[] = sprintf(
				'<span aria-current="%s" class="page-numbers current">%s</span>',
				esc_attr( $args['aria_current'] ),
				$args['before_page_number'] . number_format_i18n( $n ) . $args['after_page_number']
			);

			$dots = true;
		else :
			if ( $args['show_all'] || ( $n <= $end_size || ( $current && $n >= $current - $mid_size && $n <= $current + $mid_size ) || $n > $total - $end_size ) ) :
				$link = str_replace( '%_%', 1 == $n ? '' : $args['format'], $args['base'] );
				$link = str_replace( '%#%', $n, $link );
				if ( $add_args ) {
					$link = add_query_arg( $add_args, $link );
				}
				$link .= $args['add_fragment'];

				$page_links[] = sprintf(
					'<a class="page-numbers" href="%s">%s</a>',
					/** This filter is documented in wp-includes/general-template.php */
					esc_url( apply_filters( 'paginate_links', $link ) ),
					$args['before_page_number'] . number_format_i18n( $n ) . $args['after_page_number']
				);

				$dots = true;
			elseif ( $dots && ! $args['show_all'] ) :
				$page_links[] = '<span class="page-numbers dots">' . __( '&hellip;' ) . '</span>';

				$dots = false;
			endif;
		endif;
	endfor;

	if ( $args['prev_next'] && $current && $current < $total ) :
		$link = str_replace( '%_%', $args['format'], $args['base'] );
		$link = str_replace( '%#%', $current + 1, $link );
		if ( $add_args ) {
			$link = add_query_arg( $add_args, $link );
		}
		$link .= $args['add_fragment'];

		$page_links[] = sprintf(
			'<a class="next page-numbers" href="%s">%s</a>',
			/** This filter is documented in wp-includes/general-template.php */
			esc_url( apply_filters( 'paginate_links', $link ) ),
			$args['next_text']
		);
	endif;

	switch ( $args['type'] ) {
		case 'array':
			return $page_links;

		case 'list':
			$r .= "<ul class='page-numbers'>\n\t<li>";
			$r .= implode( "</li>\n\t<li>", $page_links );
			$r .= "</li>\n</ul>\n";
			break;

		default:
			$r = implode( "\n", $page_links );
			break;
	}

	/**
	 * Filters the HTML output of paginated links for archives.
	 *
	 * @since 5.7.0
	 *
	 * @param string $r    HTML output.
	 * @param array  $args An array of arguments. See paginate_links()
	 *                     for information on accepted arguments.
	 */
	$r = apply_filters( 'paginate_links_output', $r, $args );

	return $r;
}
51 комментарий
Полезные 3Вопросы 2 Все
    Войти