Szukałem rozwiązania jak zaimplementować znaczniki i klasy Bootstrapa w nawigacji po stronach posta w WordPressie. Znalazłem przeróżne, głównie skomplikowane filtry lub klasy i postanowiłem że sam coś napiszę. Domyślnie WP wyświetla napis 'Starsze posty' i 'Nowsze posty', albo to co podamy w argumentach funkcji. Można dodać klasy, ale tak czy inaczej dostaniemy taką surową formę:
<div class="nav-links">
<div class="nav-previous">
<a href="https://akademia.gaum.prostykod.pl/page/3/?s=zarz%C4%85dzanie" >Starsze wpisy</a>
</div>
<div class="nav-next">
<a href="https://akademia.gaum.prostykod.pl/?s=zarz%C4%85dzanie" >Nowsze wpisy</a>
</div>
</div>
Mi zależy na czymś takim jak na zdjęciu:

Zupełnie inna struktura kodu. Postanowiłem zatem wykorzystać oryginalną funkcję get_the_posts_navigation() do stworzenia podobnej, ale tworzącej kod wg Bootstrapa i z dodatkową obsługą numerów stron.
function sb5_get_the_posts_navigation( $args = array() ) {
global $paged, $wp_query;
$max_page = $wp_query->max_num_pages;
$nextpage = (int) $paged + 1;
if ($max_page < 2) return '';
$args = wp_parse_args(
$args,
array(
'prev_text' => __( 'Older posts' ),
'next_text' => __( 'Newer posts' ),
'screen_reader_text' => __( 'Posts navigation' ),
'aria_label' => __( 'Posts' ),
'class' => 'page-link',
'numbers' => false
)
);
$navigation = '';
// Don't print empty markup if there's only one page.
if ( $max_page > 1 ) {
$navigation = '<nav aria-label="' . $args["aria_label"] . '">
<ul class="pagination justify-content-center">';
// Make sure the nav element has an aria-label attribute: fallback to the screen reader text.
if ( ! empty( $args['screen_reader_text'] ) && empty( $args['aria_label'] ) ) {
$args['aria_label'] = $args['screen_reader_text'];
}
if ( ! is_single() && ( $nextpage <= $max_page ) ) {
$next_link = '<a href="' . next_posts( $max_page, false ) . '" class="' . $args["class"] . '">' . preg_replace( '/&([^#])(?![a-z]{1,8};)/i', '&$1', $args["next_text"] ) . '</a>';
}
if ( ! is_single() && $paged > 1 ) {
$prev_link = '<a href="' . previous_posts( false ) . '" class="' . $args["class"] . '">' . preg_replace( '/&([^#])(?![a-z]{1,8};)/i', '&$1', $args["prev_text"] ) . '</a>';
}
if ( isset($prev_link) ) {
$navigation .= '<li class="page-item">' . $prev_link . '</li>';
}
else
{
$navigation .= '<li class="page-item disabled">
<a class="' . $args["class"] . '">' . $args["prev_text"] . '</a>
</li>';
}
if ($args["numbers"] && $max_page > 2 && $max_page < 7)
{
for ($i=1; $i<=$max_page; $i++)
{
if ($paged != $i)
{
$navigation .= '<li class="page-item"><a class="'. $args["class"] . '" href="' .get_pagenum_link($i). '">' . $i . '</a></li>';
}
else
{
$navigation .= '<li class="page-item active"><span class="page-link">' . $i . '</span></li>';
}
}
}
else if ($args["numbers"] && $max_page >= 7)
{
if ($paged >= 3)
$navigation .= '<li class="page-item"><a class="' . $args["class"] . '" href="' .get_pagenum_link(1). '">1</a></li>';
if ($paged >= 4)
{
$navigation .= '<li class="page-item "><span class="page-link">...</span></li>';
}
$last_step = 2;
if ($paged == 1) $last_step = 3;
$first_step = 1;
if ($paged == $max_page) $first_step = 2;
if ($paged >= 1)
for ($i = $paged-$first_step; $i<$paged+$last_step; $i++)
{
if ($i <= 0 || ($i == $max_page && $i!=$paged) || $i > $max_page) continue;
if ($paged != $i)
{
$navigation .= '<li class="page-item"><a class="' . $args["class"] . '" href="' .get_pagenum_link($i). '">' .$i. '</a></li>';
}
else
{
$navigation .= '<li class="page-item active"><span class="page-link">' . $i . '</span></li>';
}
}
if (($max_page - $paged) >= 3)
{
$navigation .= '<li class="page-item "><span class="page-link">...</span></li>';
}
if ($paged < $max_page)
$navigation .= '<li class="page-item"><a class="' . $args["class"] . '" href="' .get_pagenum_link($max_page). '">' . $max_page . '</a></li>';
}
if ( isset($next_link) ) {
$navigation .= '<li class="page-item">' . $next_link . '</li>';
}
else
{
$navigation .= '<li class="page-item disabled">
<a class="' . $args["class"] . '">' . $args["next_text"] . '</a>
</li>';
}
$navigation .= '</ul></nav>';
}
return $navigation;
}
Omówienie kodu
W odróżnieniu od oryginału, moja funkcja jest bardziej samodzielna, ale do tego potrzebuje dostęp do dwóch zmiennych globalnych: $paged – numer aktualnej strony, i $wp_query – obiekt zawierający wynik zapytania do bazy WP.
$max_page
Dzięki dostępowi do $wp_query wiemy ile jest wszystkich stron i możemy zadecydować, czy wyświetlenie linków jest sensowne i jaką formę powinno przybrać. Wartość $wp_query->max_page przypisujemy do $max_page i w linii nr 7 sprawdzamy czy stron jest więcej niż jedna. Jeżeli nie, to kończymy funkcję zwracając pusty ciąg. Żaden znacznik nie zostanie wyświetlony.
$nextpage
Warto od razu wiedzieć jaki numer będzie miała następna strona. Choćby po to, by sprawdzić, czy nie wychodzi poza zakres, czyli jest większy niż $max_page.
$args
Od linii nr 9 zaczyna się sprawdzanie przypisanych paramentów. Dzięki funkcji wp_parse_args() jesteśmy w stanie dopisać domyślne wartości dla niepodanych wartości tablicy $args. Dla przykładu nie trzeba podawać wartości dla nazwy linków. System podstawi wartości domyślne. W odróżnieniu od oryginału zmodyfikowana została wartość dla klucza 'class', na bootstrapowe 'page-link'. Od siebie dodałem 'numbers', czyli boolowski parametr decydujący, czy mają być wyświetlone linki z numerami stron. Domyślnie nie będą wyświetlane.
$navigation
Zmienna przechowująca kod HTML odpowiedzialny za nawigację z parametrami Bootstrapa. Jeżeli wartość $max_page jest większa niż 1 system rozpocznie przypisywanie kodu do $navigation. W liniach 26, 27 oraz 120 mamy obudowę dla paginacji.
$next_link i $prev_link
Od 34 do 39 linii mamy kod odpowiedzialny za wygenerowanie kodu HTML dla znacznika A, czyli linku "wstecz" i "do przodu". Dla poprawnego wygenerowania linku dla następnej strony musimy sprawdzić czy wyświetlana strona nie jest stroną pojedynczego posta oraz czy numer następnej strony nie jest większy niż $max_page. W tym celu korzystamy z funkcji is_single() i porównania $nextpage <= $max_page. Jeżeli warunki są spełnione, tworzymy zmienną $next_link i przypisujemy kod HTML.
W linii 35 zwróć uwagę na funkcję next_posts(). Jej wynikiem jest link url do następnej strony. Ma dwa argumenty: $max_page i $echo. Drugi odpowiada za wyświetlanie wartości linku. Jeżeli wpiszemy 'false', to funkcja wyświetli wartość zamiast przypisać ją do ciągu $next_link. W linii 38 jest podobna funkcja previous_posts(), odpowiedzialna za adres url do poprzedniej strony. Różni się tym, że ma tylko argument $echo.
Między liniami 42 i 50 system sprawdza czy zmienna $prev_link istnieje. Jeżeli tak, to do $navigation przypisana wartość $prev_link obudowana kodem linii listy punktowanej '<li>'. Jeżeli nie istnieje $prev_link, to do $navigation przypisywany jest kod HTML z wygaszonym, nieaktywnym linkiem.
Podobnie jest w liniach 110 – 118. Tam sprawdzamy czy istnieje $next_link.
$args["numbers"]
Jeżeli chcemy wyświetlić linki z numerami stron, co jest bardzo pomocne przy większej ilości stron, to wystarczy przypisać wartość 'true' do parametru $args["numbers"]. Za to rozwiązanie odpowiada najdłuższa część kodu – od linii 52 do 107.
Za wyświetlenie linku do strony o konkretnym numerze odpowiada funkcja get_pagenum_link().
Numeracja ma sens jeżeli stron jest więcej niż dwie. W mojej funkcji zastosowałem ograniczenie, które sprawdza ile jest stron i jeżeli jest mniej niż 7, to wyświetla wszystkie linki. Jeżeli jest inaczej, to wyświetla pierwszą i ostatnia stroną oraz aktualną, poprzednią i następną. Zapobiega to wynaturzeniom przy bardzo dużej ilości stron. Pomyśl jak wyglądałaby paginacja dla stu stron?
W pierwszym przypadku zadanie jest proste. Dzięki pętli for dodajemy kolejne linki do $navigation. W linii 59 sprawdzamy czy wartość pętli odpowiada numerowi strony. Jeżeli nie, to dodajemy zwykły link. Jeżeli tak, to musimy dodać klasę 'active' do pozycji listy '<li>'. Trudniej się robi, gdy stron jest minimum 7.
Krok po kroku
- Zaczynamy od sprawdzenia czy numer aktualnej strony to co najmniej 3. Jeżeli tak, to wyświetlamy link dla pierwszej strony. Jeżeli strona ma numer mniejszy od 3, to link do strony 1 zostanie dodany w poniższej pętli 'for'.
- Dalej czy strona jest większa czy równa 4. Jeżeli tak, to musimy dodać trzykropek czyli przerywnik, który odizoluje link do strony 1 od pozostałych.
- Tworzymy dwie nowe wartości: $last_step = 2 i $first_step = 1. Jeżeli jesteśmy na pierwszej stronie, to $last_step zwiększa wartość do 3, a jeżeli na ostatniej to rośnie wartość $first_step do 2. Wartości te są pomocne przy logice odpowiedzialnej za wyświetlanie linków do pierwszej lub ostatniej strony w pętli 'for'.
- W linii 87 jest kluczowa pętla 'for' dla linków numerycznych. Wartość początkowa obliczona jest na różnicę między numerem strony i $first_step. Wartość końcowa to suma numeru strony i $last_step.
- Dodatkowe zabezpieczenie jest poniżej. Jeżeli wartość pętli to 0 lub mniej albo numer strony jest równy $max_page i jednocześnie numer strony nie jest wartością pętli albo wartość pętli jest większa od $max_page, to krok pętli jest pomijany. Wystarczy, że jeden z trzech warunków jest spełniony.
- Następnie pozostaje sprawdzić czy wartość pętli jest równa numerowi strony. Wtedy przypisujemy nieaktywny link, a jeżeli nie, to zwykły.
- Na koniec system sprawdza czy numer strony jest mniejszy od $max_page i jeżeli to prawda, to wyświetla link do ostatniej strony.
Podsumowanie
I na tym koniec. Funkcję można rozbudować od większą liczbę numerów stron lub dodatkowe kroki dla bardzo dużej ilości stron. Jeżeli mamy np. 30 stron, to przejście z pierwszej strony do 15 teraz trochę potrwa.