Standard WordPress-funksjonen the_category() er praktisk i klassiske temaer, men den har en hard grense: den skriver alltid ut HTML-ankere (<a href="...">...</a>) til kategoriarkivet. Det funker på en enkeltinnleggsside der leseren skal kunne klikke videre. Det funker dårlig på et porteføljekort, en slider, en meta-rad i et headless-oppsett eller en badge der kategorien bare skal være etikett.
Løsningen er get_the_category(), som returnerer en array av term-objekter i stedet for ferdig markup. Offisiell referanse: get_the_category(), the_category() og get_the_terms().
På WordUp Oslo og WordCamp Norge dukker dette opp ofte når noen bygger et kortbasert arkiv for norske medlemsbedrifter eller et konferanseprogram: de vil ha «Nyheter», «Case» eller «Arrangører» som tekst, ikke som klikkbare arkivlenker inne i kortet. Samme krav dukker opp i medlemsportaler der kortet allerede er hele lenkemålet - en ekstra anker inne i badge-en skaper nestede <a>-tagger, som er ugyldig HTML og rotete for tastaturnavigasjon.
Målet med denne veiledningen er praktisk: velg riktig API, escape i riktig kontekst, håndter flere termer, egne taksonomier, SEO-primary og FSE, og unngå snarveier som ser smarte ut i et gist men feiler i produksjon.
Hvorfor the_category() alltid skriver ut anker
the_category() er en template tag i WordPress-forstand: den er laget for å vise noe, ikke for å returnere data du kan forme videre. Under panseret bygger den på get_the_category_list(), som setter sammen en liste med lenker, separator og valgfri overordnet kontekst.
Parametere styrer separator, foreldre og post-ID. De styrer ikke en «kun tekst»-modus. Hvis du trenger navnet uten markup, er feil verktøy valgt. Bytt API, ikke lim på strip_tags() etterpå.
Et konkret scenario: du har en forsiderad med åtte arrangementskort etter WordCamp Norge. Hvert kort linker allerede til single. Hvis badge-en også er en kategoriarkiv-lenke, får mobilbrukere to treffområder oppå hverandre, og analytics blander «kortklikk» med «kategoriklikk». Ren tekst i badge-en gjør målingen ærlig.
<?php
// Dette skriver alltid ut <a>-tagger.
the_category( ', ' );
?>Formen på objektet fra get_the_category()
<?php
$categories = get_the_category();
// Array av WP_Term-objekter for taksonomien «category».
?>get_the_category( $post_id = false ) uten argument bruker gjeldende post fra den globale $post (inne i loopen). Returverdien er en array av term-objekter. Feltene du bruker mest:
name- synlig etikett («Nyheter»)slug- URL- og CSS-vennlig streng (nyheter)term_id- stabil ID for spørringer og metadescription- kategoriens beskrivelsesfeltcount- antall innlegg i kategorienparent- overordnet term-ID (0 hvis toppnivå)
Tom array er lovlig: et innlegg uten kategorier (eller et custom post type uten category) gir array(). Sjekk alltid ! empty( $categories ) før du leser indeks 0. Ellers får du notices i WP_DEBUG og blanke badges i produksjon når redaksjonen glemmer å tilordne kategori på et hastig publisert innlegg.
Rekkefølgen i arrayen er ikke en kontrakt du bør bygge forretningslogikk på. Den følger hvordan termene er tilordnet og sortert internt, ikke nødvendigvis den redaksjonelle «hovedkategorien». Trenger du den, les plugin-meta for primary (se egen seksjon under).
Utenfor hovedloopen (widget, REST-callback, cron) send eksplisitt post-ID:
<?php
$categories = get_the_category( $post_id );
?>Escaping: esc_html, esc_attr og utgangskontekst
Kategorinavn kommer fra databasen og kan i teorien inneholde tegn som ødelegger markup. Bruk kontekst riktig:
- Tekstinnhold i HTML:
esc_html( $category->name ) - Attributter (
class,data-*,title):esc_attr( $category->slug ) - URL-er (hvis du velger å lenke selv):
esc_url( get_category_link( $category->term_id ) )
<?php
$cats = get_the_category();
$first_cat = ! empty( $cats ) ? $cats[0] : null;
if ( $first_cat ) : ?>
<span class="badge badge-<?php echo esc_attr( $first_cat->slug ); ?>">
<?php echo esc_html( $first_cat->name ); ?>
</span>
<?php endif; ?>Da får «Nyheter» klassen .badge-nyheter uten at du mister kontroll over markup. the_category() gir deg ikke den friheten.
Flere kategorier: foreach og implode
Mange innlegg har mer enn én kategori. Samle navnene først, slå dem sammen etterpå. Da styrer du separator, mellomrom og rekkefølge uten å røre anker-HTML.
<?php
$categories = get_the_category();
$output = array();
if ( ! empty( $categories ) ) {
foreach ( $categories as $category ) {
$output[] = esc_html( $category->name );
}
echo implode( ', ', $output );
}
?>Tips fra praksis på norske nettredaksjoner: bruk en midterprikk (·) eller skråstrek når komma konkurrerer med tusenskiller i talltunge case-studier. implode() bryr seg ikke - du velger strengen.
Hvis du trenger både navn og slug i samme pass (for eksempel JSON-LD articleSection pluss CSS), bygg to arrays eller ett assosiativt kart i loopen, ikke kall get_the_category() to ganger i samme request uten grunn.
Antipattern: strip_tags på kategorilisten
Dette dukker fortsatt opp i gamle child themes og i «raske» snippets fra forum:
<?php
// Unngå dette.
echo strip_tags( get_the_category_list( ', ' ) );
?>Hvorfor det er dårlig:
- Du ber WordPress bygge full HTML bare for å kaste den.
strip_tags()fjerner ikke attributter før den stripper tagger i alle versjoner/kontekster like forutsigbart som du tror når noe annet filtrerer listen.- Du mister tilgang til
slug,term_idogparent. - Filtre på
the_category/get_the_category_listkan fortsatt kjøre og endre output før strip.
Riktig retning: hent data med get_the_category() eller get_the_terms(), escape ved utskrift. Bygg markup selv hvis du trenger den.
Egne taksonomier: get_the_terms()
get_the_category() er spesialisert for innebygd category. Har du produktlinje, fagfelt eller wordcamp-spor, bruk get_the_terms():
<?php
$terms = get_the_terms( get_the_ID(), 'fagfelt' );
if ( empty( $terms ) || is_wp_error( $terms ) ) {
return;
}
$names = array();
foreach ( $terms as $term ) {
$names[] = esc_html( $term->name );
}
echo implode( ', ', $names );
?>Merk forskjellen: get_the_terms() kan returnere false eller WP_Error. get_the_category() returnerer vanligvis en (muligens tom) array. Kode som kopieres mellom kategori og custom taxonomy må håndtere begge returformer.
Til WordCamp Norge-programmet er det vanlig å ha både category (innholdstype) og en egen taksonomi for spor (utvikling, innhold, business). Da trenger du to kall, ikke én «superliste».
Primærkategori fra SEO-plugins
Yoast, Rank Math og flere lagrer en «primary category». get_the_category()[0] er ikke garantert den samme. Rekkefølgen fra get_the_category() følger term-rekkefølge / tilordning, ikke nødvendigvis SEO-pluginens valg.
Typisk mønster (Yoast-meta som eksempel - sjekk alltid nøkkelen for din plugin-versjon):
<?php
$post_id = get_the_ID();
$primary = (int) get_post_meta( $post_id, '_yoast_wpseo_primary_category', true );
$categories = get_the_category( $post_id );
$chosen = null;
if ( $primary && ! empty( $categories ) ) {
foreach ( $categories as $category ) {
if ( (int) $category->term_id === $primary ) {
$chosen = $category;
break;
}
}
}
if ( ! $chosen && ! empty( $categories ) ) {
$chosen = $categories[0];
}
if ( $chosen ) {
echo esc_html( $chosen->name );
}
?>Fallback til første element er viktig: eldre innlegg mangler ofte primary-meta. På et medlemsmagasin etter WordUp Bergen så vi badges som hoppet mellom «Medlemsnytt» og «Arrangement» fra side til side fordi noen kort leste [0] og andre leste Yoast-primary uten fallback.
Blokktémaer og FSE: hvor PHP fortsatt trengs
I Full Site Editing får du Query Loop og Post Terms-blokker. De er fine når du vil ha standard term-lenker i et arkiv. De er svake når:
- badge-markup må matche et designsystem med faste BEM-klasser
- du trenger bare én term (primær) uten lenke
- headless / App Router-frontends leser REST og forventer rene strenger
- du mapper term-slug til Tailwind- eller token-klasser i PHP-delen av et hybridtema
Da beholder du en liten template-part eller et blokkvariasjonsmønster med PHP, eller du eksponerer feltene via REST (register_rest_field) basert på samme get_the_category() / get_the_terms()-logikk. Poenget er det samme: les term-objektet, ikke strip ferdig HTML.
Et hybridtema vi så etter WordUp Trondheim brukte FSE til layout og en render_callback på en egen «Post badge»-blokk. Callback-en kalte get_the_category( $block->context['postId'] ), escapet name/slug, og returnerte span-markup. Redaktørene flyttet blokken i Site Editor; utviklerne beholdt kontroll på klassenavn. Det er et bedre kompromiss enn å låse hele arkivet til klassisk PHP, eller å tvinge Post Terms til å late som den er et designsystem.
Når du faktisk vil ha lenken
Noen ganger er ankeret riktig: brødsmuler, term-sky, enkeltinnlegg der meta-raden skal lede til arkivet. Da er the_category() eller en bevisst bygget <a href="<?php echo esc_url( get_category_link( $id ) ); ?>"> greit. Poenget er ikke «aldri lenke». Poenget er å ikke la template-taggen bestemme markup når UI-et ditt allerede har bestemt noe annet.
Caching av kategorinavn under meta og kort
På forsider med 24 kort er 24 kall til get_the_category() billige sammenlignet med remote HTTP, men de summerer seg under objektcache-miss og tung term-meta. Noen mønstre som holder i produksjon:
- Les inne i loopen, skriv én gang. Ikke kall funksjonen i header, i kortet og i schema for samme post uten å cache lokalt i en variabel.
- Transient per post bare når du aggregérer tungt. For eksempel en «topp kategorier denne uken»-widget. Ikke transient for enkeltnavn på single - det er overkill.
- Object cache / Redis: WordPress cacher allerede mange term-oppslag. Unngå å invalidere hele term-cachen unødig etter hver lagring i admin.
- Fragment cache for HTML-kort: hvis kortet er dyrt (bilde + ACF + term), cache hele kort-HTML med post-ID som nøkkel og tøm ved
save_post.
<?php
function wppoland_post_category_label( $post_id = 0 ) {
$post_id = $post_id ? (int) $post_id : get_the_ID();
$cats = get_the_category( $post_id );
if ( empty( $cats ) ) {
return '';
}
return esc_html( $cats[0]->name );
}
?>En liten helper hindrer at fem template-filer hver finner opp sin egen variant med eller uten escaping.
Ferdig snippet til single.php og kort
Minimal utgave til single eller content.php:
<?php
$categories = get_the_category();
if ( ! empty( $categories ) ) {
echo esc_html( $categories[0]->name );
}
?>Liste uten lenker:
<?php
$categories = get_the_category();
$output = array();
if ( ! empty( $categories ) ) {
foreach ( $categories as $category ) {
$output[] = esc_html( $category->name );
}
echo implode( ', ', $output );
}
?>Trenger du samme kontroll gjennom hele temaet, er det typisk slik vi strukturerer merkelapper i skreddersydd WordPress-utvikling.
Sjekkliste før du merger
- Bytt
the_category()der du faktisk ikke vil ha anker. - Bruk
get_the_category()forcategory,get_the_terms()for resten. - Escape med
esc_html/esc_attretter kontekst. - Bygg lister med
foreach+implode, ikkestrip_tags( get_the_category_list() ). - Avklar om «første kategori» eller SEO-primary er kravet.
- Test et innlegg uten kategori (tom array) og et CPT uten
category. - I FSE: beholde PHP bare der blokken ikke kan levere etiketten din.
Oppsummering
the_category() printer lenker. get_the_category() gir deg term-objekter. Derfra bygger du tekst, badges, schema og REST-felt uten å strippe HTML i etterkant. Samme idé gjelder egne taksonomier via get_the_terms(). Primærkategori er plugin-meta, ikke magisk indeks null.
Ta med dette videre i code review:
- Ingen
strip_tagsrundt kategorilister. - Escape ved utskrift, ikke «senere».
- Tom array og
WP_Errorer testet. - Primary vs
[0]er avklart med redaksjonen. - FSE-blokker får PHP der designet krever det.
På WordCamp Norge- og WordUp-oppsett er det forskjellen mellom et kort som ser ut som designet og et kort som lekker arkivlenker inn i UI-et. Hold API-valget eksplisitt, så slipper neste utvikler å gjette hvorfor badge-en plutselig ble klikkbar igjen etter en theme-oppdatering.







