Alternative to the paginate_links() pagination function
This function always returns HTML, even if parameter type=array you get an array of ready-made <a> tags. This may not work when you need to completely change the HTML structure of your pagination. Below is a small function that returns an array of objects instead of 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;
}
What the function outputs:
$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] =>
)
)
*/
Now use this function in the loop:
<?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_current ) { ?>
<strong><?php _e( $link->page_num ) ?></strong>
<?php } else { ?>
<a href="<?php esc_attr_e( $link->url ) ?>"><?php _e( $link->page_num ) ?></a>
<?php } ?>
</li>
<?php } ?>
</ul>
<?php
}
We get it:
<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>
—
Note embeded into: paginate_links()