Vis gjeldende og overordnet kategori i WordPress

Vis gjeldende og overordnet kategori i WordPress

Sist verifisert: 21. september 2026
12 min lesetid
Veiledning
Full-stack-utvikler
Når du bygger egne WordPress-temaer for norske redaksjoner, trenger du ofte mer enn en flat kategoriliste. Brødsmuler (breadcrumbs) som viser både gjeldende kategori og dens overordnede gir leseren kontekst, og gir søkemotorer en tydelig sti gjennom innholdsklyngen.

På WordCamp Norge og WordUp Oslo er dette et tema som dukker opp hver gang noen bytter fra «standard kategoriwidget» til et skreddersydd arkiv. Spørsmålet er sjelden «finnes det en funksjon?» - det er «hvilken kategori skal styre stien når innlegget ligger i tre grener samtidig, og hvordan får vi Nynorsk-arkivtitler til å matche Bokmål-brødsmuler uten å hardkode strenger?».

WordPress lagrer hierarki i parent-feltet på term_taxonomy, men get_the_category() returnerer en flat tabell. Denne veiledningen går gjennom fellene, de innebygde hjelpefunksjonene, Primary Category fra Yoast og Rank Math, Schema.org BreadcrumbList, egendefinerte taksonomier og caching - med PHP du kan lime inn i et child theme.

#Hvordan WordPress lagrer kategorihierarki

Kategorier er den innebygde hierarkiske taksonomien category. I motsetning til stikkord (post_tag) kan hvert term ha en overordnet (parent).

I databasen er dette ikke en kompleks graf. Hver rad i wp_term_taxonomy har et parent-felt med term_id til direkte forelder. Toppnivå (for eksempel «Teknologi») har parent = 0. Barnet «WordPress» peker på Teknologi, og barnebarnet «Gutenberg» peker på WordPress.

Det betyr at WordPress ikke lagrer en ferdig, flat sti i ett felt. Hvert steg oppover treet krever et oppslag i object cache eller database. Når du bygger brødsmuler, er jobben din å gå den stien deterministisk - ikke å håpe at indeks null i get_the_category() er riktig gren.

På norske redaksjonsnettsteder ser vi ofte tre nivåer: fagområde → undertema → format (for eksempel «Offentlig sektor» → «Digitalisering» → «Case»). Arkivmalen for /category/digitalisering/ må da vise både overordnet og gjeldende term. Hvis du bare printer $categories[0]->name, mister leseren konteksten som gjorde at de klikket inn fra forsiden eller et WordCamp Norge-sammendrag.

Et praktisk tips fra WordUp Oslo-sesjoner: tegn treet på en whiteboard før du koder. Redaktører tenker i «mapper», utviklere tenker i term_id. Når de to modellene ikke matcher, ender du med brødsmuler som peker til en søstergren fordi noen huket av feil avkrysningsboks i Gutenberg.

#Fallgruver med get_the_category()

Inne i The Loop er det fristende å starte her:

$categories = get_the_category( $post->ID );

Du får en tabell med WP_Term-objekter. To problemer dukker opp raskt:

  1. Ingen hierarkisk sortering. Har innlegget «Teknologi», «Programmering» og «PHP», sorterer WordPress vanligvis etter navn eller ID - ikke etter dybde i treet.
  2. Flere grener samtidig. En redaktør kan huke av både «PHP» og «Bransjenyheter». Hvilken skal styre stien over tittelen?

Naiv bruk av $categories[0] gir brødsmuler som hopper når alfabetet eller term_id endrer rekkefølgen. På flerspråklige norske nettsteder merkes det ekstra: Bokmål-etiketten «Nyheter» og Nynorsk-etiketten «Nyhende» kan bytte plass i sorteringen selv om strukturen er den samme.

// Unngå dette som eneste kilde til "gjeldende" kategori.
$maybe_primary = $categories[0] ?? null;

Et annet fall: get_the_category() respekterer ikke orderby du kanskje forventer fra get_terms(). Den er en tynn wrapper rundt get_the_terms( $post_id, 'category' ). Hvis du trenger eksplisitt sortering (for eksempel etter dybde), må du gjøre det selv etter at tabellen er hentet.

Når innlegget ikke har noen kategori, returnerer funksjonen en tom tabell - ikke false. Sjekk alltid empty( $categories ) før du leser indeks. På eldre temaer som antar at «Uncategorized» alltid finnes, krasjer brødsmulen stille når redaksjonen har slettet standardkategorien og glemt å tilordne en ny.

#Metode 1: get_category_parents()

WordPress har en hjelpefunksjon laget for akkurat denne jobben: get_category_parents(). Signaturen ser slik ut:

get_category_parents(
    int $category_id,
    bool $display_link = false,
    string $separator = '/',
    bool $nice_name = false,
    array $deprecated = array()
): string|WP_Error

#Enkel bruk

function wppoland_vis_enkel_hierarki( int $post_id ): void {
    $categories = get_the_category( $post_id );

    if ( empty( $categories ) ) {
        return;
    }

    $current = $categories[0];
    $parents = get_category_parents(
        $current->term_id,
        true,
        ' <span class="sep">&raquo;</span> '
    );

    if ( is_wp_error( $parents ) ) {
        return;
    }

    echo '<div class="breadcrumbs-simple">';
    echo '<a href="' . esc_url( home_url( '/' ) ) . '">' . esc_html__( 'Forside', 'wppoland' ) . '</a>';
    echo ' <span class="sep">&raquo;</span> ';
    echo $parents; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- WP genererer anker.
    echo '<span class="current-title">' . esc_html( get_the_title( $post_id ) ) . '</span>';
    echo '</div>';
}

Bruk esc_html__() (eller tilsvarende) for «Forside» slik at et Nynorsk-tema kan oversette etiketten uten å røre PHP-logikken. På WordUp Oslo-notater ser man ofte at arkivmalen er oversatt, mens brødsmulen fortsatt sier «Home» fordi noen hardkodet engelsk streng i header.php.

#Begrensninger

get_category_parents() er praktisk, men:

  • Den er låst til category, ikke custom taxonomies.
  • HTML-en er en streng - vanskelig å cache per segment eller legge til aria-current.
  • Den løser ikke Primary Category-konflikten. Du må fortsatt velge riktig $category_id først.
  • Med $nice_name = true får du slug i stedet for visningsnavn - nyttig for debug, sjelden for brukervendt UI.

Hvis du trenger å hoppe over toppnivået (noen design vil starte på barn, ikke «Forside » Teknologi»), er heller ikke denne funksjonen egnet. Da er get_ancestors() + egen filtrering av første ledd raskere enn å parse HTML-strengen med regex.

#Metode 2: get_ancestors() for full kontroll

get_ancestors() er den mer fleksible veien. Den fungerer for alle hierarkiske taksonomier og returnerer en tabell med forfedre-ID-er (nærmeste forelder først).

function wppoland_bygg_kategori_sti( int $term_id ): array {
    $ancestor_ids = get_ancestors( $term_id, 'category' );
    $ancestor_ids = array_reverse( $ancestor_ids ); // rot → blad

    $trail = array();

    foreach ( $ancestor_ids as $ancestor_id ) {
        $term = get_category( (int) $ancestor_id );
        if ( $term && ! is_wp_error( $term ) ) {
            $trail[] = $term;
        }
    }

    $current = get_category( $term_id );
    if ( $current && ! is_wp_error( $current ) ) {
        $trail[] = $current;
    }

    return $trail;
}

#Render med semantisk HTML

function wppoland_render_breadcrumb_html( array $trail, int $post_id ): string {
    if ( empty( $trail ) ) {
        return '';
    }

    $html  = '<nav class="breadcrumbs" aria-label="' . esc_attr__( 'Brødsmuler', 'wppoland' ) . '">';
    $html .= '<ol class="breadcrumbs__list">';

    $html .= '<li class="breadcrumbs__item">';
    $html .= '<a href="' . esc_url( home_url( '/' ) ) . '">' . esc_html__( 'Forside', 'wppoland' ) . '</a>';
    $html .= '</li>';

    foreach ( $trail as $term ) {
        $html .= '<li class="breadcrumbs__item">';
        $html .= '<a href="' . esc_url( get_category_link( $term->term_id ) ) . '">';
        $html .= esc_html( $term->name );
        $html .= '</a></li>';
    }

    $html .= '<li class="breadcrumbs__item breadcrumbs__item--current" aria-current="page">';
    $html .= esc_html( get_the_title( $post_id ) );
    $html .= '</li>';

    $html .= '</ol></nav>';

    return $html;
}

ol + aria-label er mer tilgjengelig enn en flat div med &raquo;. Skjermlesere får en liste, og du kan style separatoren med CSS ::before i stedet for hardkodede tegn.

Sett sammen Primary Category-valget og stien i én helper som single-malen kaller:

function wppoland_single_category_breadcrumb( int $post_id ): string {
    $primary = wppoland_finn_primaer_kategori( $post_id );
    if ( ! $primary ) {
        return '';
    }

    $trail = wppoland_bygg_kategori_sti( (int) $primary->term_id );
    $html  = wppoland_render_breadcrumb_html( $trail, $post_id );
    $html .= wppoland_breadcrumb_json_ld( $trail, $post_id );

    return $html;
}

Kall den fra single.php (eller via the_content-filter hvis du av en grunn ikke eier malen). Unngå å duplisere JSON-LD i både tema og SEO-plugin.

#Primary Category: Yoast og Rank Math

Når innlegget har flere kategorier, må du velge én gren. SEO-plugins lagrer dette som post meta:

  • Yoast SEO: _yoast_wpseo_primary_category
  • Rank Math: rank_math_primary_category
function wppoland_finn_primaer_kategori( int $post_id ): ?WP_Term {
    $primary_id = (int) get_post_meta( $post_id, '_yoast_wpseo_primary_category', true );

    if ( ! $primary_id ) {
        $primary_id = (int) get_post_meta( $post_id, 'rank_math_primary_category', true );
    }

    if ( $primary_id ) {
        $term = get_category( $primary_id );
        if ( $term && ! is_wp_error( $term ) ) {
            return $term;
        }
    }

    // Fallback: dypeste node i treet.
    $categories = get_the_category( $post_id );
    if ( empty( $categories ) ) {
        return null;
    }

    $deepest = null;
    $max_depth = -1;

    foreach ( $categories as $cat ) {
        $depth = count( get_ancestors( $cat->term_id, 'category' ) );
        if ( $depth > $max_depth ) {
            $max_depth = $depth;
            $deepest   = $cat;
        }
    }

    return $deepest;
}

Dypeste node er et fornuftig fallback når Primary Category mangler: den speiler vanligvis den mest spesifikke redaksjonelle plasseringen. Hvis to kategorier har samme dybde, vinner den som kommer sist i løkken - dokumenter det for redaksjonen, eller sorter eksplisitt på term_id for stabilitet.

Yoast og Rank Math kan begge være aktive i migreringsperioder. Les Yoast først bare hvis det er prosjektets sannhetskilde; ellers bytt rekkefølge. Aldri bland: hvis Yoast har en Primary Category og Rank Math en annen, velg én plugin som eier feltet og deaktiver den andres breadcrumb-modul.

For redaktøropplæring: Primary Category er ikke «viktigste SEO-nøkkelord» i markedsføringsspråk - det er «hvilken gren i treet skal brødsmulen følge». Det skillet sparer timer med «hvorfor peker stien til Nyheter når artikkelen handler om PHP».

#Schema.org BreadcrumbList i JSON-LD

Synlig HTML er ikke nok hvis du vil at Google skal forstå stien. Schema.org BreadcrumbList i JSON-LD er standardformatet.

#HTML5-skisse

<nav class="breadcrumbs" aria-label="Brødsmuler">
  <ol>
    <li><a href="https://eksempel.no/">Forside</a></li>
    <li><a href="https://eksempel.no/kategori/teknologi/">Teknologi</a></li>
    <li><a href="https://eksempel.no/kategori/teknologi/wordpress/">WordPress</a></li>
    <li aria-current="page">Slik bygger du brødsmuler</li>
  </ol>
</nav>

#JSON-LD-generator

function wppoland_breadcrumb_json_ld( array $trail, int $post_id ): string {
    $items   = array();
    $position = 1;

    $items[] = array(
        '@type'    => 'ListItem',
        'position' => $position++,
        'name'     => __( 'Forside', 'wppoland' ),
        'item'     => home_url( '/' ),
    );

    foreach ( $trail as $term ) {
        $items[] = array(
            '@type'    => 'ListItem',
            'position' => $position++,
            'name'     => $term->name,
            'item'     => get_category_link( $term->term_id ),
        );
    }

    $items[] = array(
        '@type'    => 'ListItem',
        'position' => $position,
        'name'     => get_the_title( $post_id ),
        'item'     => get_permalink( $post_id ),
    );

    $graph = array(
        '@context'        => 'https://schema.org',
        '@type'           => 'BreadcrumbList',
        'itemListElement' => $items,
    );

    return '<script type="application/ld+json">' .
        wp_json_encode( $graph, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE ) .
        '</script>';
}

Hold HTML og JSON-LD i sync. Hvis den synlige stien hopper over «Forside», skal JSON-LD også gjøre det. Dobbel BreadcrumbList (tema + Yoast) gir konflikt - slå av den ene.

Valider med Googles Rich Results Test etter deploy. Typiske feil: position som starter på 0, manglende absolutt URL i item, eller name som er tom fordi get_the_title() kjørte utenfor loop. Bruk alltid eksplisitt $post_id.

På flerspråklige oppsett (Polylang / WPML): term-lenker må speile aktivt språk. get_category_link() respekterer vanligvis språkpluginens filtre, men hardkodede home_url( '/kategori/...' )-strenger gjør det ikke. Hold deg til API-ene.

#Egendefinerte taksonomier

Mange norske innholdssider bruker register_taxonomy() for «tema», «region» eller «fagområde» i stedet for bare category. Da bytter du:

$ancestors = get_ancestors( $term_id, 'fagomrade' );
$term      = get_term( $term_id, 'fagomrade' );
$link      = get_term_link( $term );

get_category_parents() finnes ikke for custom taxonomies. Bruk get_ancestors() + get_term() + get_term_link(), og sjekk is_wp_error() på lenken. Hierarkiske custom taxonomies oppfører seg som kategorier i parent-modellen, så Primary Category-mønsteret kan gjenskapes med egen post meta-nøkkel hvis redaksjonen trenger det.

Eksempel på en gjenbrukbar helper:

function wppoland_bygg_taksonomi_sti( int $term_id, string $taxonomy ): array {
    if ( ! taxonomy_exists( $taxonomy ) ) {
        return array();
    }

    $ancestor_ids = array_reverse( get_ancestors( $term_id, $taxonomy ) );
    $trail        = array();

    foreach ( $ancestor_ids as $ancestor_id ) {
        $term = get_term( (int) $ancestor_id, $taxonomy );
        if ( $term && ! is_wp_error( $term ) ) {
            $trail[] = $term;
        }
    }

    $current = get_term( $term_id, $taxonomy );
    if ( $current && ! is_wp_error( $current ) ) {
        $trail[] = $current;
    }

    return $trail;
}

Husk: hierarchical => false i register_taxonomy() betyr at parent alltid er 0. Da gir get_ancestors() en tom tabell, og brødsmulen blir bare ett ledd. Det er korrekt oppførsel - ikke en bug.

#Ytelse og caching

På arkivsidene med mange innlegg koster gjentatte get_ancestors()-kall. Object cache (Redis/Memcached via Drop-in) hjelper fordi get_term() allerede er cachet, men du kan fortsatt memo-isere per request:

function wppoland_cached_trail( int $term_id ): array {
    static $cache = array();

    if ( isset( $cache[ $term_id ] ) ) {
        return $cache[ $term_id ];
    }

    $cache[ $term_id ] = wppoland_bygg_kategori_sti( $term_id );
    return $cache[ $term_id ];
}

For fragment-cache (for eksempel i en full page cache foran PHP): cache nøkkelen bør inkludere post_id, Primary Category-ID og språk/locale. Et Bokmål-innlegg og et Nynorsk-arkiv som deler samme term_id men ulike etiketter via språkplugin, må ikke dele samme HTML-fragment.

Transient-cache av hele brødsmulestrengen er sjelden verdt det på enkeltinnlegg. Transient-er betaler seg når du bygger dyre, flernivå-menyer for hele treet - ikke for én sti på single.php.

Invalidér når term endres. Hvis du likevel cacher HTML, heng deg på edited_term og set_object_terms:

add_action( 'edited_category', 'wppoland_flush_breadcrumb_cache' );
add_action( 'set_object_terms', 'wppoland_flush_breadcrumb_cache' );

function wppoland_flush_breadcrumb_cache(): void {
    // Slett gruppe-nøkler eller bump en cache-versjon i et options-felt.
    delete_option( 'wppoland_breadcrumb_cache_ver' );
    add_option( 'wppoland_breadcrumb_cache_ver', (string) time(), '', false );
}

En versjonsbump i cache-nøkkelen er enklere enn å spore hver post_id som bruker et gitt term.

#Vanlige spørsmål

#Hvordan henter jeg gjeldende kategori?

Kall get_the_category( $post_id ). Velg deretter Primary Category eller den dypeste noden før du bygger stien. Ikke stol på indeks null alene.

#Hva er forskjellen på get_category_parents() og get_ancestors()?

get_category_parents() returnerer en ferdig streng for category. get_ancestors() returnerer ID-er for alle hierarkiske taksonomier. Bruk sistnevnte når du eier markup, schema og caching selv.

#Hva gjør jeg når flere kategorier ligger på samme nivå?

Sett Primary Category i Yoast eller Rank Math. Uten det: sorter deterministisk (for eksempel høyeste term_id) og dokumenter valget for redaksjonen, så stien ikke hopper mellom publiseringer.

#Må BreadcrumbList inkludere innlegget?

Speil det brukeren ser. Hvis HTML ender på tittelen, skal JSON-LD gjøre det samme. Ikke legg til skjulte noder «for SEO».

#Påvirker brødsmuler rangering?

De er primært UX og struktur. Korrekt BreadcrumbList kan gi rikere visning i søk. Dårlig synkronisering mellom HTML og JSON-LD gir støy, ikke magiske posisjoner.

#Oppsummering og neste steg

  1. Ikke bruk $categories[0] som eneste sannhet.
  2. Foretrekk get_ancestors() når du trenger kontroll; bruk get_category_parents() for raske prototyper.
  3. Les Primary Category-meta fra Yoast eller Rank Math, med dybde-fallback.
  4. Emitter nav/ol og matchende BreadcrumbList JSON-LD.
  5. For custom taxonomies: samme mønster med get_term / get_term_link.

Flernivå kategorihierarkier er der arkivmaler og brødsmuler sprekker først. Trenger du hjelp til å få hierarkiet riktig på tvers av tema og SEO-plugins, ta kontakt via WordPress-utvikling for arkiver og maler.

Neste steg

Gjør artikkelen om til faktisk implementering

Denne blokken styrker intern lenking og sender leseren videre til de mest relevante tjenestene og innholdet.

Vil du få dette implementert på nettstedet ditt?

Hvis du vil gjøre kunnskapen i artikkelen om til konkrete forbedringer, redesign eller en tydelig leveranseplan, kan jeg ta det videre.

Relevant klynge

Utforsk andre WordPress-tjenester og kunnskapsbase

Styrk virksomheten din med profesjonell teknisk støtte innen kjerneområdene i WordPress-økosystemet.

Hvordan henter jeg gjeldende kategori i WordPress?#
Bruk get_the_category() inne i The Loop (eller send post-ID eksplisitt). Funksjonen returnerer en tabell med WP_Term. Den første indeksen er ikke nødvendigvis den du vil vise i brødsmuler - sjekk Primary Category eller dybde i treet før du bygger stien.
Hvordan viser jeg overordnet kategori også?#
Når du har term_id, bruk get_category_parents() for en ferdig HTML-streng, eller get_ancestors() for en tabell med forfedre-ID-er du kan mappe selv. For flere nivåer er get_ancestors() mer forutsigbar fordi du styrer markup og separator selv.
Er dette nyttig for breadcrumb-navigasjon?#
Ja. En egen, lett PHP-sti i temaet unngår tunge plugins når du bare trenger kategori → overordnet → innlegg. Kombiner med BreadcrumbList JSON-LD slik at synlig HTML og strukturerte data stemmer overens.
Hva er forskjellen på get_category_parents() og get_ancestors()?#
get_category_parents() er kategori-spesifikk og returnerer en streng (ofte med lenker). get_ancestors() er generisk for alle hierarkiske taksonomier og returnerer en tabell med ID-er. Bruk sistnevnte når du trenger egen HTML, caching eller custom taxonomies.
Må BreadcrumbList inkludere selve innlegget?#
Google anbefaler at ListItem-rekkefølgen speiler det brukeren ser. Hvis den synlige stien ender på innleggstittelen, bør JSON-LD gjøre det samme. Ikke legg til ekstra noder som ikke finnes i HTML.

Trenger du FAQ tilpasset bransje og marked? Vi lager en versjon som støtter dine forretningsmål.

Ta kontakt

Relaterte artikler