paginate_links()
Позволяет создать ссылки пагинации для любых страниц.
Пример того как выглядит планация: « предыдущая 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
.
строку
- HTML или строку ссылок пагинации. Приtype = plain
илиtype = list
. По умолчанию.массив
- Массив данных ссылок пагинации для дальнейшей обработки в PHP. Приtype = array
.null
- Когда общее количество страниц пагинации меньше 2.
Шаблон использования
$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> для стилизации.
Вообще, эти параметры были созданы для того, чтобы указать текст для роботов, чтобы при просмотре кода, было понятно для чего предназначены ссылки.
Примеры
#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();
#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
. Потому что cpage
будет перенаправление со страницы пагинации на саму запись, а сpaged
будет 404 ошибка на странице пагинации. -
Для получения номера страницы пагинации вместо привычного
get_query_var( 'paged' )
используемget_query_var( 'page' )
. - Этот код не будет работать на страницах у которых контент делиться на несколько страниц тегом <!--nextpage-->, подробнее см. здесь.
#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>
#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 ) ); ?>
#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. |