Częstym błędem w projektach motywów WordPress jest zdawanie się na proste, płaskie listowanie kategorii lub instalowanie ciężkich, zewnętrznych wtyczek tylko po to, by wygenerować trzy poziomy odnośników tekstowych. W tym przewodniku inżynieryjnym krok po kroku wyjaśniam, jak działa model danych taksonomii w WordPressie, jak prawidłowo przejść drzewo przodków za pomocą wbudowanych funkcji get_ancestors() oraz get_category_parents(), jak rozwiązać konflikt wielokrotnych kategorii (tzw. Primary Category) oraz jak wygenerować czysty kod HTML zintegrowany ze schematem Schema.org BreadcrumbList.
Jak WordPress przechowuje hierarchię kategorii w bazie danych
Kategorie w WordPressie są wbudowaną taksonomią hierarchiczną o nazwie systemowej category. W przeciwieństwie do tagów (post_tag), każdy wpis kategorii może posiadać przypisaną kategorię nadrzędną (parent).
W warstwie bazy danych MySQL relacja ta nie jest skomplikowanym grafem, lecz prostym odniesieniem klucza obcego w tabeli wp_term_taxonomy. Każdy rekord posiada pole parent, które przechowuje identyfikator term_id bezpośredniego rodzica. Jeśli kategoria leży na najwyższym poziomie drzewa (np. “Technologia”), wartość pola parent wynosi 0. Kategoria potomna (np. “Programowanie”) posiada jako parent identyfikator “Technologii”, a jej kategoria podrzędna (np. “PHP”) wskazuje na “Programowanie”.
Zrozumienie tej struktury jest kluczowe: WordPress nie przechowuje pełnej, spłaszczonej ścieżki w jednym polu. Każde przejście w górę drzewa wymaga odpytania pamięci podręcznej obiektów (Object Cache) lub bazy danych o kolejne rekordy nadrzędne.
Jakie pułapki ma get_the_category() w breadcrumbach
Gdy deweloper chce pobrać kategorię wpisu wewnątrz pętli (The Loop), pierwszym naturalnym odruchem jest wywołanie:
$categories = get_the_category( $post->ID );Funkcja ta zwraca tablicę obiektów WP_Term. Jednakże napotykam tu na dwa poważne problemy architektoniczne:
- Brak porządku hierarchicznego: Jeśli do wpisu przypisano zarówno kategorię “Technologia”, “Programowanie”, jak i “PHP”, WordPress zwróci je posortowane domyślnie według nazwy (
name), bo rdzeń wypełnia cache termów zorderby => name, a nie według ich pozycji w drzewie taksonomii. - Wielokrotne przypisanie do różnych gałęzi: Redaktor może przypisać wpis do kategorii “PHP” oraz jednocześnie do kategorii “Aktualności branżowe”. Która z nich powinna wyznaczać ścieżkę nawigacyjną nad tytułem wpisu?
Naiwne pobranie pierwszego elementu tablicy $categories[0] prowadzi do losowego wyświetlania okruszków w zależności od tego, która kategoria ma wcześniejszą literę w alfabecie.
Jak wykluczyć kategorię Bez kategorii z okruszków
Nowa instalacja przypisuje wpisy do kategorii domyślnej. Gdy redakcja później przenosi wpis do właściwego działu, kategoria domyślna często zostaje jako drugie przypisanie. Ponieważ tablica jest sortowana według nazwy, okruszki oparte na $categories[0] potrafią wtedy wrócić do „Bez kategorii”. Odfiltruj term zapisany w get_option( 'default_category' ), zanim wybierzesz liść, albo wymagaj ustawienia głównej kategorii w procesie redakcyjnym.
Jak użyć get_category_parents() do ścieżki kategorii
WordPress posiada dedykowaną funkcję pomocniczą przeznaczoną właśnie do generowania łańcucha rodziców: get_category_parents(). Funkcja przyjmuje pięć parametrów, przy czym piąty, $deprecated, od wersji 4.8.0 nie jest używany i wywołuje komunikat o przestarzałym argumencie, jeśli cokolwiek przekażesz:
get_category_parents( int $category_id, bool $display_link = false, string $separator = '/', bool $nice_name = false, array $deprecated = array() ): string|WP_ErrorProsty przykład użycia get_category_parents()
function wppoland_wyswietl_prosta_hierarchie( int $post_id ): void {
$categories = get_the_category( $post_id );
if ( empty( $categories ) ) {
return;
}
// Wybieram pierwszą kategorię $current_category = $categories[0];
// Pobieram rodziców wraz z linkami HTML $parents = get_category_parents( $current_category->term_id, true, ' <span class="sep">»</span> ' );
if ( ! is_wp_error( $parents ) ) {
echo '<div class="breadcrumbs-simple">';
echo '<a href="' . esc_url( home_url( '/' ) ) . '">Strona główna</a> <span class="sep">»</span> ';
echo $parents;
echo '<span class="current-title">' . esc_html( get_the_title( $post_id ) ) . '</span>';
echo '</div>';
}
}Ograniczenia get_category_parents()
Choć funkcja ta działa szybko, ma poważne wady we współczesnym frontendzie:
- Zwraca surowy ciąg znaków HTML z prostymi znacznikami
<a>, uniemożliwiając nadanie klas CSS poszczególnym elementom<li>czy<span>. - Nie pozwala na dodanie atrybutów mikrodanych Schema.org ani dostępności ARIA (
aria-current="page"). - Ostatni element w zwracanym ciągu jest linkiem do samej kategorii bieżącej, a separator jest doklejany również na samym końcu.
Dlatego w profesjonalnych projektach stosuje się podejście tablicowe oparte na funkcji get_ancestors().
Jak użyć get_term_parents_list() zamiast get_category_parents()
Od WordPressa 4.8.0 get_category_parents() jest tylko nakładką: wywołuje get_term_parents_list() z taksonomią category. Ta sama funkcja działa z każdą taksonomią hierarchiczną, także z product_cat z WooCommerce. Argumenty to format (name albo slug), separator, link (domyślnie true) oraz inclusive (domyślnie true, czyli z samym termem na końcu).
$list = get_term_parents_list(
$term_id,
'product_cat',
array(
'inclusive' => false,
'separator' => ' / ',
)
);
if ( $list && ! is_wp_error( $list ) ) {
echo wp_kses_post( rtrim( $list, ' /' ) );
}Kod źródłowy w category-template.php pokazuje dwie osobliwości. Nazwa trafia do linku bez esc_html(), przez esc_url() przechodzi tylko adres. Separator jest doklejany po każdym elemencie, również po ostatnim, dlatego przykład wywołuje rtrim(). Z 'inclusive' => false pomijasz bieżący term, na przykład gdy i tak stoi niżej jako nagłówek. Ochronę przed uszkodzonym łańcuchem rodziców (term wskazujący po błędnym imporcie na własnego potomka) zapewnia get_ancestors(): funkcja przerywa pętlę, gdy identyfikator rodzica pojawi się drugi raz.
Jak użyć get_ancestors() do precyzyjnej ścieżki kategorii
Funkcja get_ancestors() jest znacznie bardziej elastyczna. Zwraca tablicę identyfikatorów (term_id) wszystkich przodków danego obiektu, począwszy od bezpośredniego rodzica aż do korzenia drzewa.
$ancestor_ids = get_ancestors( $term_id, 'category', 'taxonomy' );Ponieważ tablica jest ułożona od dołu do góry (od rodzica do dziadka), wystarczy odwrócić jej kolejność za pomocą array_reverse(), aby uzyskać naturalną sekwencję nawigacyjną: korzeń, podkategoria, kategoria bieżąca.
Oto kompletna implementacja zorientowana na czysty kod i pełną kontrolę nad znacznikami:
/**
* Zwraca ustrukturyzowaną tablicę elementów ścieżki kategorii dla danego wpisu.
*
* @param int $post_id Identyfikator wpisu.
* @return array<int, array{id: int, name: string, url: string}>
*/
function wppoland_pobierz_drzewo_kategorii( int $post_id ): array {
$categories = get_the_category( $post_id );
if ( empty( $categories ) ) {
return [];
}
// Wybór kategorii o największym zagłębieniu
$deepest_category = null;
$max_depth = -1;
foreach ( $categories as $cat ) {
$ancestors = get_ancestors( $cat->term_id, 'category', 'taxonomy' );
$depth = count( $ancestors );
if ( $depth > $max_depth ) {
$max_depth = $depth;
$deepest_category = $cat;
}
}
if ( ! $deepest_category instanceof WP_Term ) {
return [];
}
$breadcrumb_items = [];
// Pobieram przodków wybranej najgłębszej kategorii $ancestors = array_reverse( get_ancestors( $deepest_category->term_id, 'category', 'taxonomy' ) );
foreach ( $ancestors as $ancestor_id ) {
$ancestor_term = get_term( $ancestor_id, 'category' );
if ( $ancestor_term instanceof WP_Term ) {
$link = get_term_link( $ancestor_term );
if ( ! is_wp_error( $link ) ) {
$breadcrumb_items[] = [
'id' => (int) $ancestor_term->term_id,
'name' => $ancestor_term->name,
'url' => $link,
];
}
}
}
// Dodaję bieżącą kategorię $current_link = get_term_link( $deepest_category ); if ( ! is_wp_error( $current_link ) ) {
$breadcrumb_items[] = [
'id' => (int) $deepest_category->term_id,
'name' => $deepest_category->name,
'url' => $current_link,
];
}
return $breadcrumb_items;
}Dzięki wyliczeniu liczby przodków w pętli (count($ancestors)), funkcja automatycznie preferuje kategorię najbardziej specyficzną. Jeśli wpis przypisano do “Blog” (0 przodków) i “WordPress > Optymalizacja” (1 przodek), skrypt bezbłędnie wybierze “Optymalizację”.
Jak wyświetlić aktualną kategorię na stronie archiwum kategorii
Na stronie archiwum kategorii get_the_category() nie pomoże: każdy wpis na liście może mieć kilka kategorii i żadna z nich nie musi być archiwum, które właśnie oglądasz. Kategoria archiwum to obiekt zapytania. is_category() potwierdza kontekst, get_queried_object() zwraca WP_Term archiwum, a get_queried_object_id() sam jego identyfikator.
if ( is_category() ) {
$term = get_queried_object();
foreach ( array_reverse( get_ancestors( $term->term_id, 'category', 'taxonomy' ) ) as $ancestor_id ) {
$ancestor = get_term( $ancestor_id, 'category' );
if ( $ancestor && ! is_wp_error( $ancestor ) ) {
printf(
'<a href="%s">%s</a> <span aria-hidden="true">/</span> ',
esc_url( get_term_link( $ancestor ) ),
esc_html( $ancestor->name )
);
}
}
echo '<h1>' . esc_html( $term->name ) . '</h1>';
}Ścieżka nad nagłówkiem powstaje tu z termu archiwum, więc wpis przypisany do trzech gałęzi nie może jej przesunąć. Do samego nagłówka wystarczy też single_cat_title(), które wypisuje nazwę bieżącego archiwum kategorii.
Jak wyświetlić podkategorie aktualnej kategorii
Odwrotnym kierunkiem do rodzica jest lista dzieci. wp_list_categories() przyjmuje child_of (wszyscy potomkowie termu) i depth (ile poziomów wypisać). Pusty title_li usuwa element listy z domyślnym nagłówkiem „Kategorie”, więc znacznik ul ustawiasz sam.
if ( is_category() ) {
echo '<ul class="subcategories">';
wp_list_categories(
array(
'child_of' => get_queried_object_id(),
'depth' => 1,
'title_li' => '',
)
);
echo '</ul>';
}Dwa szczegóły z wartości domyślnych: hide_empty ma wartość 1, więc puste podkategorie znikają, dopóki nie przekażesz 'hide_empty' => 0. A child_of bez depth wypisuje całe poddrzewo, nie tylko bezpośredni poziom. Gdy potrzebujesz danych zamiast HTML, get_categories( array( 'parent' => $id ) ) zwraca tylko bezpośrednie dzieci, a get_term_children( $id, 'category' ) identyfikatory wszystkich potomków na każdym poziomie.
Jak sprawdzić, czy wpis jest w podkategorii
in_category() przechodzi przez has_category() do is_object_in_term(), a ta funkcja porównuje tylko termy przypisane do wpisu bezpośrednio. Wpis przypisany wyłącznie do „WordPress” zwraca false dla in_category( 'technologia' ), nawet jeśli „WordPress” jest dzieckiem „Technologii”. Przy warunkach typu „gdziekolwiek w tej gałęzi” sprawdź także przodków:
/**
* True if the post is in $parent_id or in any of its descendants.
*/
function wppoland_in_category_branch( $parent_id, $post_id = false ) {
$parent_id = (int) $parent_id;
foreach ( get_the_category( $post_id ) as $term ) {
if ( $term->term_id === $parent_id || cat_is_ancestor_of( $parent_id, $term ) ) {
return true;
}
}
return false;
}cat_is_ancestor_of() wywołuje term_is_ancestor_of() z taksonomią category i rekurencyjnie wspina się po rodzicach. W niestandardowych taksonomiach wywołaj bezpośrednio term_is_ancestor_of( $rodzic, $dziecko, $taksonomia ). To samo sprawdzenie przydaje się w menu bocznym: element nadrzędny nie traci stanu aktywnego, gdy czytelnik jest w archiwum wnuczki.
Jak rozwiązać konflikt wielu kategorii (Primary Category)
W profesjonalnych serwisach opartych o WordPress redaktorzy często korzystają z wtyczek SEO (takich jak Yoast SEO, Rank Math czy SEOPress), które wprowadzają koncepcję Głównej Kategorii (Primary Category). Pozwala ona jawnie wskazać, która ścieżka URL i breadcrumb są właściwe dla wpisu przypisanego do wielu działów.
Ignorowanie tej wartości w autorskim motywie prowadzi do niespójności między okruszkami w treści a mapą strony czy kanonicznymi adresami URL.
Oto jak rozbudować logikę wyboru kategorii o odczyt metadanych wtyczek SEO:
/**
* Pobiera obiekt kategorii wiodącej (Primary Category) dla danego wpisu.
*
* @param int $post_id
* @return WP_Term|null
*/
function wppoland_wyznacz_glowna_kategorie( int $post_id ): ?WP_Term {
// 1. Sprawdź Yoast SEO
$yoast_primary_id = get_post_meta( $post_id, '_yoast_wpseo_primary_category', true );
if ( ! empty( $yoast_primary_id ) ) {
$term = get_term( (int) $yoast_primary_id, 'category' );
if ( $term instanceof WP_Term ) {
return $term;
}
}
// 2. Sprawdź Rank Math
$rank_math_primary_id = get_post_meta( $post_id, 'rank_math_primary_category', true );
if ( ! empty( $rank_math_primary_id ) ) {
$term = get_term( (int) $rank_math_primary_id, 'category' );
if ( $term instanceof WP_Term ) {
return $term;
}
}
// 3. Fallback: znajdź kategorię o największej głębokości w taksonomii
$categories = get_the_category( $post_id );
if ( empty( $categories ) ) {
return null;
}
$best_category = null;
$max_depth = -1;
foreach ( $categories as $cat ) {
$depth = count( get_ancestors( $cat->term_id, 'category', 'taxonomy' ) );
if ( $depth > $max_depth ) {
$max_depth = $depth;
$best_category = $cat;
}
}
return $best_category;
}Dzięki takiemu mechanizmowi kod zachowuje pełną elastyczność: respektuje decyzję edytora w panelu wpisu, a w przypadku braku zainstalowanej wtyczki SEO stosuje bezpieczny algorytm maksymalnego zagnieżdżenia.
Jak zbudować semantyczny HTML i Schema.org BreadcrumbList
Standardowa nawigacja okruszkowa powinna spełniać wymogi semantyki HTML5, wytycznych dostępności cyfrowej WCAG oraz specyfikacji wyszukiwarki Google dotyczącej danych uporządkowanych.
Struktura HTML5 breadcrumbów zgodna z WCAG
<nav class="breadcrumb-navigation" aria-label="Ścieżka powrotna">
<ol class="breadcrumb-list">
<li class="breadcrumb-item"><a href="/">Start</a></li>
<li class="breadcrumb-item"><a href="/kategoria/programowanie/">Programowanie</a></li>
<li class="breadcrumb-item"><a href="/kategoria/programowanie/php/">PHP</a></li>
<li class="breadcrumb-item is-active" aria-current="page">Aktualny artykuł</li>
</ol>
</nav>Kluczowe elementy dostępności:
- Znacznik
<nav>z atrybutemaria-label="Ścieżka powrotna"lubaria-label="Breadcrumb". - Uporządkowana lista
<ol>, która informuje technologie asystujące (czytniki ekranu) o kolejności i liczbie kroków w ścieżce. - Atrybut
aria-current="page"na ostatnim elemencie tekstowym oznaczający aktualnie przeglądaną stronę.
BreadcrumbList w formacie JSON-LD
Chociaż mikrodane Microdata można wstrzykiwać bezpośrednio w atrybuty tagów HTML (itemscope, itemtype), Google oficjalnie rekomenduje format JSON-LD. Jest on całkowicie odseparowany od warstwy prezentacji CSS, co eliminuje ryzyko błędów parsowania przy zmianie layoutu strony.
Oto gotowa funkcja renderująca kompletny zestaw: semantyczny HTML oraz powiązany skrypt JSON-LD:
function wppoland_render_pelne_okruszki(): void {
if ( ! is_single() ) {
return;
}
global $post;
$main_category = wppoland_wyznacz_glowna_kategorie( $post->ID );
$breadcrumbs = [
[
'name' => 'Strona główna',
'url' => home_url( '/' ),
]
];
if ( $main_category instanceof WP_Term ) {
$ancestor_ids = array_reverse( get_ancestors( $main_category->term_id, 'category', 'taxonomy' ) );
foreach ( $ancestor_ids as $ancestor_id ) {
$term = get_term( $ancestor_id, 'category' );
if ( $term instanceof WP_Term ) {
$breadcrumbs[] = [
'name' => $term->name,
'url' => get_term_link( $term ),
];
}
}
$breadcrumbs[] = [
'name' => $main_category->name,
'url' => get_term_link( $main_category ),
];
}
// Dodaję bieżący wpis $breadcrumbs[] = [ 'name' => get_the_title( $post->ID ), 'url' => get_permalink( $post->ID ), ];
// 1. Renderowanie kodu HTML
echo '<nav class="breadcrumbs" aria-label="Ścieżka powrotna">';
echo '<ol class="breadcrumbs-list">';
$total = count( $breadcrumbs );
foreach ( $breadcrumbs as $index => $item ) {
$is_last = ( $index === $total - 1 );
echo '<li class="breadcrumbs-item' . ( $is_last ? ' is-active' : '' ) . '">';
if ( ! $is_last ) {
echo '<a href="' . esc_url( $item['url'] ) . '">' . esc_html( $item['name'] ) . '</a>';
echo ' <span class="breadcrumbs-separator" aria-hidden="true">/</span> ';
} else {
echo '<span aria-current="page">' . esc_html( $item['name'] ) . '</span>';
}
echo '</li>';
}
echo '</ol>';
echo '</nav>';
// 2. Renderowanie Schema.org JSON-LD
$schema_items = [];
foreach ( $breadcrumbs as $index => $item ) {
$schema_items[] = [
'@type' => 'ListItem',
'position' => $index + 1,
'name' => $item['name'],
'item' => $item['url'],
];
}
$schema_data = [
'@context' => 'https://schema.org',
'@type' => 'BreadcrumbList',
'itemListElement' => $schema_items,
];
echo '<script type="application/ld+json">' . wp_json_encode( $schema_data, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE ) . '</script>';
}Co Google pokazuje z BreadcrumbList
Dokumentacja Google o okruszkach ustala minimum: BreadcrumbList z co najmniej dwoma elementami ListItem, position od 1 i adresem URL w item przy każdym elemencie. Wyjątkiem jest ostatni element: jeśli brakuje w nim item, Google używa adresu samej strony. Dla wpisu strona główna plus jedna kategoria spełnia już minimum, a tytuł wpisu jako ostatni element jest dozwolony, ale nie wymagany.
Google dopuszcza kilka ścieżek dla jednej strony i zaleca ścieżkę, która odpowiada typowej drodze użytkownika, zamiast powielać strukturę adresu URL. Dla okruszków kategorii to argument za główną kategorią: opisuje drogę zamierzoną przez redakcję, nawet gdy permalink nie zawiera żadnej kategorii.
W wyszukiwarce Google ścieżka jest dziś widoczna tylko na komputerach. Od 23 stycznia 2025 wyniki mobilne pokazują w wierszu adresu wyłącznie domenę, co ogłosił blog Search Central. Znaczniki są nadal obsługiwane, raport w Search Console zostaje. Widoczne okruszki w motywie i tak służą przede wszystkim czytelnikom.
Jak obsłużyć niestandardowe taksonomie w breadcrumbach
Jeśli w projekcie korzystasz z własnych typów wpisów (CPT), np. “Katalog produktów” czy “Portfolio”, relacje taksonomiczne nie korzystają z wbudowanej taksonomii category.
Funkcja get_ancestors() jest uniwersalna: trzeci parametr określa typ obiektu (taxonomy), a drugi nazwę samej taksonomii:
// Przykład dla taksonomii 'typ_produktu' w sklepie
$term_id = 42;
$ancestors = get_ancestors( $term_id, 'typ_produktu', 'taxonomy' );Do pobrania termów przypisanych do wpisu w niestandardowej taksonomii zamiast get_the_category() należy użyć funkcji get_the_terms():
$terms = get_the_terms( $post->ID, 'typ_produktu' );
if ( ! empty( $terms ) && ! is_wp_error( $terms ) ) {
$first_term = $terms[0];
// Dalsza logika analogiczna jak dla kategorii
}Jak użyć bloku Okruszki z WordPressa 7.0 z główną kategorią
Od WordPressa 7.0 rdzeń zawiera własny blok core/breadcrumbs, w polskim edytorze nazwany „Okruszki” (aktualne wydanie według API wersji WordPress.org to 7.1.3). Plik block.json wymienia atrybuty separator (domyślnie /), showHomeItem, showCurrentItem, showOnHomePage i prefersTaxonomy. Na wpisie bez wpisu nadrzędnego blok buduje ścieżkę z termów taksonomii: bierze pierwszą taksonomię zarejestrowaną dla typu treści, która ma termy, a z niej pierwszy term zwrócony przez get_the_terms(), przez reset( $terms ). To ta sama pułapka co $categories[0]: bez ingerencji wygrywa pierwsza nazwa w kolejności alfabetycznej.
Kod źródłowy bloku udostępnia do tego filtr block_core_breadcrumbs_post_type_settings. Oczekuje tablicy z kluczami taxonomy i term, przy czym term to slug, a nie identyfikator. Tak przekażesz do bloku główną kategorię z Yoast SEO albo Rank Math:
add_filter(
'block_core_breadcrumbs_post_type_settings',
static function ( $settings, $post_type, $post_id ) {
if ( 'post' !== $post_type ) {
return $settings;
}
$primary = (int) get_post_meta( $post_id, '_yoast_wpseo_primary_category', true );
if ( ! $primary ) {
$primary = (int) get_post_meta( $post_id, 'rank_math_primary_category', true );
}
$term = $primary ? get_term( $primary, 'category' ) : null;
if ( $term && ! is_wp_error( $term ) ) {
$settings['taxonomy'] = 'category';
$settings['term'] = $term->slug;
}
return $settings;
},
10,
3
);Drugi filtr, block_core_breadcrumbs_items, pozwala zmienić gotową listę par label i url przed renderowaniem. Znaczniki bloku spełniają podstawy dostępności: nav z aria-label, lista ol i ostatni element jako span z aria-current="page". Blok nie wypisuje JSON-LD. BreadcrumbList nadal pochodzi z wtyczki SEO albo z funkcji takiej jak powyższa, a oba źródła powinny używać tego samego termu, który przekazujesz blokowi przez filtr.
Jak dbać o wydajność i cache breadcrumbów kategorii
Częstym pytaniem deweloperów jest wpływ dynamicznego trawersowania drzewa kategorii na czas generowania strony (TTFB).
WordPress wyposażony jest w wewnętrzny mechanizm pamięci podręcznej termów. Wywołanie get_the_category() oraz get_ancestors() korzysta z wbudowanego cache taksonomii. Przy poprawnie skonfigurowanym Object Cache (Redis lub Memcached) odpytania te nie generują dodatkowych zapytań SQL do bazy danych podczas renderowania pojedynczego wpisu.
Jeśli jednak w szablonie wyświetlasz listę 50 wpisów na stronie głównej i dla każdego z nich chcesz wyliczać pełne okruszki, warto skorzystać z funkcji update_object_term_cache() lub funkcji get_the_terms() w masowym zapytaniu, aby uniknąć problemu N+1 zapytań do bazy danych.
Jak pobrać kategorie wpisu poza pętlą
Widżety, shortcode’y i callbacki bloków często działają poza główną pętlą. Wtedy globalny $post wskazuje inny wpis albo jest pusty, a get_the_category() bez argumentu zwraca kategorie niewłaściwego wpisu. Przekazuj identyfikator jawnie: get_the_category( $post_id ), a w kontrolerach REST API zawsze identyfikator z $request albo z obiektu wpisu. To samo dotyczy kart wpisów w archiwum: po przesunięciu pętli każda karta musi pytać o kategorie własnego wpisu.
Jak budować okruszki kategorii z Polylang i WPML
Identyfikatory termów zależą od języka. Ścieżka zbudowana w jednym języku i wyrenderowana w drugim pokaże złe etykiety. Najpierw ustal przetłumaczony identyfikator termu przez API wtyczki, dopiero potem wywołaj get_ancestors(). Permalinki podążają za strukturą adresów języka: get_term_link() to respektuje, zakodowane na sztywno ścieżki nie. Klucz cache okruszków musi zawierać język, inaczej ścieżka jednej wersji językowej przyklei się do drugiej. Sprawdź też, czy meta głównej kategorii z Yoast lub Rank Math po tłumaczeniu wskazuje term w tym samym języku.
Jak budować okruszki dla stron i kategorii produktów WooCommerce
Hierarchia stron (post_parent), kategorie sklepu WooCommerce i niestandardowe typy treści dokumentacyjnych zwykle potrzebują własnych ścieżek. Nie mieszaj w jednym okruszku rodzica wpisu i rodzica kategorii, chyba że architektura informacji jest tak zaplanowana. Częsty błąd w sklepach: okruszki motywu czytają category, a produkty żyją w product_cat. Widoczna ścieżka pokazuje działy bloga, schema pokazuje kategorie produktów, a Search Console zgłasza niespójne okruszki. Jedna taksonomia na typ treści, jeden budowniczy ścieżki, jeden blok schema.
Jak przetestować okruszki kategorii przed wdrożeniem
- Wpis bez kategorii: funkcja zwraca pusty ciąg albo
false, bez komunikatu PHP. - Tylko kategoria główna: bez linku do rodzica, ale nadal poprawny BreadcrumbList ze stroną główną i termem.
- Hierarchia przeniesiona po publikacji: wyczyść cache starej i nowej ścieżki.
- Usunięty identyfikator głównej kategorii w meta Yoast lub Rank Math: powrót do
get_the_category(). - Nazwa kategorii z cudzysłowem lub ampersandem: zawsze
esc_html(). - Import z CSV albo starego CMS: identyfikator rodzica może wskazywać usunięty term, więc sprawdź wynik
get_term()i przerwij ścieżkę. - Motyw blokowy: shortcode lub część szablonu musi widzieć właściwe
get_the_ID()w kontekście bloku.
Jeśli motyw ma CI, zapisz te przypadki jako testy PHPUnit lub integracyjne. Przynajmniej raz ręcznie: wpis z dwiema kategoriami, ustawioną główną kategorią, rodzicem trzy poziomy wyżej i pustym object cache.
Najczęstsze pytania o kategorię nadrzędną wpisu
Różnica między get_category_parents() a get_ancestors()
Wpis przypisany do kategorii na tym samym poziomie
Link do bieżącego artykułu w danych BreadcrumbList
Wpływ okruszków chleba na pozycjonowanie
Podsumowanie
Wdrożenie czystej, programistycznej logiki kategorii nadrzędnych eliminuje konieczność instalowania dodatkowych wtyczek w WordPressie, dając pełną kontrolę nad kodem HTML, dostępnością cyfrową oraz danymi strukturalnymi JSON-LD.
Potrzebujesz audytu architektury taksonomii lub budujesz dedykowany motyw dla wymagającego serwisu? Sprawdź moje usługi jako profesjonalny WordPress developer lub zleć nam kompleksowy audyt kodu WordPress.







