WordPress betinget logikk for kategorier og taksonomier

WordPress betinget logikk for kategorier og taksonomier

Sist verifisert: 21. september 2026
11 min lesetid
Guide
Full-stack-utvikler

Betinget logikk i WordPress er ikke «litt PHP rundt malen». Det er kontrakten mellom spørringen, malhierarkiet og det du faktisk viser til brukeren. I 2026, med block themes og Full Site Editing, lever den kontrakten fortsatt i PHP for plugins, mu-plugins, enqueue-logikk og alle steder der en malfil eller et block pattern ikke strekker til.

Denne guiden er skrevet for utviklere som allerede kjenner The Loop, men som fortsatt møter rare false-positiver med is_front_page(), tomme resultater utenfor løkken, eller cache som serverer feil variant til innloggede brukere. Vi går fra taksonomisjekker til query-pitfalls, kapasitetskontroller, FSE-grenser, caching og hjelpere du kan unit-teste.

#Hvor conditional tags faktisk leser fra

Conditional tags i WordPress leser hovedsakelig den globale $wp_query (eller den spørringen du sender inn). De sier ikke «hvilken fil som kjører». De svarer på spørsmål som: er denne forespørselen et enkeltinnlegg, et kategoriarkiv, forsiden, et søk?

Det har to konsekvenser:

  1. Kall dem for tidlig (før wp-hooken har bygget hovedspørringen), og du får false eller uforutsigbare verdier.
  2. Kall dem inne i en sekundær WP_Query uten å sende den spørringen inn, og du leser fortsatt hovedspørringen.

Dokumentasjonen for conditional tags er eksplisitt på at mange sjekker forutsetter at hovedspørringen er klar. I praksis: tema-setup i after_setup_theme er for tidlig for is_singular(). wp_enqueue_scripts, template_redirect og malfiler er trygge nok for de fleste tilfeller. For plugins som må forgrene seg tidlig, lytt på wp eller senere, eller injiser logikken via hooks der konteksten allerede finnes.

#is_singular og is_front_page: vanlige fallgruver

#is_singular uten post type

is_singular() uten argument er true for ethvert enkelt objekt i hovedspørringen: innlegg, side, CPT. Det er nyttig når du vil laste felles CSS for alle singles. Det er farlig når du egentlig mente «bare vanlige innlegg»:

// For bredt hvis du bare vil treffe blogginnlegg.
if ( is_singular() ) {
    wp_enqueue_script( 'share-buttons' );
}

// Eksplisitt.
if ( is_singular( 'post' ) ) {
    wp_enqueue_script( 'share-buttons' );
}

is_single() er snevrere (ikke sider, ikke attachments i vanlig bruk), men for CPT er is_singular( 'product' ) mer lesbart enn en blanding av is_singular() og manuelle get_post_type()-sjekker.

#is_front_page versus is_home

Norske redaksjonsnett bruker ofte «statisk forside + innleggsside». Da er:

  • is_front_page() true på den valgte forsidesiden (eller bloggindekset hvis «dine siste innlegg» er satt som forside).
  • is_home() true på innleggsindeksen, som kan være en egen side, ikke forsiden.

En klassisk feil er å putte hero-logikk bak is_home() og tro at den treffer forsiden. En annen er å bruke is_front_page() || is_home() uten å vite hvilken av de to som skal ha hvilken layout. Skill dem i kode og i kommentar:

if ( is_front_page() && ! is_home() ) {
    // Statisk forside.
}

if ( is_home() && ! is_front_page() ) {
    // Innleggsside som ikke er forsiden.
}

#Query flags etter custom queries

Hvis du kjører query_posts() (unngå det) eller glemmer wp_reset_postdata() etter en sekundær loop, kan conditional tags fortsatt se riktig hovedspørring, mens the_post()-baserte hjelpere ser feil post. Symptom: is_singular( 'post' ) er true, men in_category( 'nyheter' ) treffer feil innlegg. Roten er nesten alltid global postkontekst, ikke selve conditional taggen.

#in_the_loop: når postkontekst finnes

in_the_loop() forteller om du er inne i hovedløkken for den aktuelle spørringen. Mange funksjoner som the_title(), the_content() og implisitte varianter av in_category() / has_term() forventer at gjeldende post er satt via the_post().

Utenfor løkken:

// Usikkert: avhenger av hvilken post som tilfeldig ligger i global $post.
if ( in_category( 'nyheter' ) ) {
    // ...
}

// Eksplisitt post-ID.
if ( has_term( 'nyheter', 'category', $post_id ) ) {
    // ...
}

I widgets, shortcodes, REST-callbacks og block render-callbacks er du ofte utenfor The Loop. Da skal alle taksonomi- og meta-sjekker ta et eksplisitt postobjekt eller ID. En praktisk regel på WPPoland-prosjekter: hvis funksjonen kan kalles fra mer enn én kontekst, er tredje argument på has_term() obligatorisk.

function wppoland_post_has_news_category( int $post_id ): bool {
    return has_term( 'nyheter', 'category', $post_id );
}

#current_user_can og innloggingsgrener

Kapasitetssjekker er betinget logikk på brukeren, ikke på spørringen. Typisk mønster:

if ( current_user_can( 'edit_post', $post_id ) ) {
    // Vis «rediger»-lenke, intern merknad, A/B-flagg for redaksjon.
}

Tre punkter som ofte glemmes:

  1. is_user_logged_in() er billig, men for grov. En abonnent og en redaktør er begge «innlogget».
  2. current_user_can( 'manage_options' ) er ikke det samme som «er administrator på dette nettstedet» i multisite.
  3. Aldri cache HTML som inneholder kapasitetsgrener som om den var anonym. Se caching-seksjonen under.

For plugins: hold capability-strenger i konstanter eller én policy-klasse, slik at du ikke sprer magiske strenger i malene. Det gjør også tester enklere fordi du kan mocke policyen uten å bootstrappe hele brukersystemet i hvert scenario.

#Malhierarki versus PHP-conditionals

Template hierarchy er WordPress sin declarative routing: single-product.php, taxonomy-product_cat.php, front-page.php, home.php, og i block themes tilsvarende HTML-maler under templates/. Når hierarkiet allerede skiller kontekst, trenger du ikke å gjenskape den med if ( is_singular( 'product' ) ) inne i en generisk single.php.

Bruk hierarkiet når:

  • hele layouten er annerledes (WooCommerce-produkt vs blogginnlegg),
  • du vil at en juniorutvikler skal finne filen uten å lese en 200-linjers if/else,
  • du jobber i et klassisk tema der filnavn fortsatt er kontrakten.

Bruk PHP-conditionals når:

  • bare én seksjon endres (disclaimer, schema, enqueue),
  • konteksten krysser flere maltyper (felles script på singular CPT A og B),
  • logikken lever i et plugin som ikke eier malfilene.

En sunn fordeling på større codebases: hierarki for makrostruktur, små helpers for mikrobeslutninger. Hvis single.php inneholder mer enn to nivåer med nøstede conditionals for post type og taksonomi, er det et signal om å splitte mal eller flytte beslutningen til en navngitt helper.

#Block themes og FSE: hva PHP fortsatt eier

I et block theme erstattes mange PHP-maler av HTML-maler og template parts. Conditional tags forsvinner ikke. De flytter seg:

  • til functions.php / et theme-plugin for wp_enqueue_scripts,
  • til block render-callbacks (render_callback) der dynamiske blokker bygges,
  • til hooks som body_class, render_block, pre_render_block.

Begrensninger du må designe rundt:

  1. En ren HTML-mal kan ikke kalle has_term() direkte. Du trenger en dynamisk blokk, et pattern med PHP via plugin, eller enqueue/body_class-logikk utenfor malen.
  2. theme.json og style variations uttrykker design-tokens, ikke forretningsregler som «vis mva-tekst bare på product_cat: mat».
  3. Site Editor lar redaktører bytte template parts visuelt. Hardkodede PHP-grener i et gammelt child theme kan da «forsvinne» fra synlig UI mens de fortsatt kjører i hooks. Dokumenter hvor beslutningen lever.

Praktisk mønster for FSE-prosjekter:

add_filter(
    'render_block',
    static function ( string $block_content, array $block ): string {
        if ( ( $block['blockName'] ?? '' ) !== 'core/group' ) {
            return $block_content;
        }
        if ( empty( $block['attrs']['className'] ) || ! str_contains( $block['attrs']['className'], 'is-news-only' ) ) {
            return $block_content;
        }
        if ( ! is_singular( 'post' ) || ! has_term( 'nyheter', 'category', get_queried_object_id() ) ) {
            return '';
        }
        return $block_content;
    },
    10,
    2
);

Klassen is-news-only blir kontrakten mellom editor og kode. Redaktøren ser gruppen; PHP bestemmer synlighet uten å duplisere hele malen.

#Taksonomier: in_category versus has_term

Tilbake til kjernen i tittelen. in_category() er en tynn wrapper rundt native category. Den forstår ikke product_cat, post_tag (bruk has_tag() / has_term()), eller egne taksonomier.

if ( in_category( 'nyheter' ) ) {
    // Bare category.
}

if ( has_term( 'jeans', 'product_cat' ) ) {
    // Fungerer for WooCommerce og andre CPT-taksonomier.
}

Foretrekk has_term() i ny kode, også for vanlige kategorier. Da er API-et likt på tvers av innholdstyper, og du kan bytte taksonominavn uten å bytte funksjon. Bruk term-ID når du har den: det unngår slug-oppslag og er mer stabilt ved rename av slug.

#Hierarki: barn er ikke forelder

has_term( 'frukt', 'category', $post_id ) er false hvis innlegget bare er merket eple, selv om eple er barn av frukt. Kjernen gjør eksakt match. For «innlegg i denne grenen av treet» trenger du en hjelper:

function wppoland_post_in_descendant_category( int $post_id, int $parent_term_id ): bool {
    $descendants = get_term_children( $parent_term_id, 'category' );
    if ( is_wp_error( $descendants ) ) {
        return false;
    }

    $term_ids = array_map( 'intval', $descendants );
    $term_ids[] = $parent_term_id;

    return has_term( $term_ids, 'category', $post_id );
}

get_term_children() er memoized via object cache i vanlige oppsett, men på store trær bør du ikke kalle den inne i en loop over tusenvis av poster uten å cache resultatet per parent-ID i requesten.

#Sidekontekst versus postdata

Skill disse to:

BehovFunksjon
Er vi på kategoriarkivet «nyheter»?is_category( 'nyheter' )
Er det aktuelle innlegget i «nyheter»?in_category() / has_term()
Er vi på et egendefinert taksonomiarkiv?is_tax( 'product_cat', 'jeans' )
Er produktet merket «jeans»?has_term( 'jeans', 'product_cat', $post_id )

WooCommerce-fellen: is_product_category() inne i en generisk Loop over produkter på forsiden er false, fordi du ikke er på kategoriarkivet. Bruk has_term() med produkt-ID.

#Caching: page cache versus innloggede brukere

Page cache (Nginx FastCGI cache, Cloudflare Cache Everything, plugin full-page cache) lagrer HTML for en URL. Conditional tags som leser spørringen er vanligvis trygge: samme URL, samme is_singular(), samme arkiv. Conditional tags som leser brukeren er det ikke.

Regler som holder i produksjon:

  1. Anonym HTML skal ikke inneholde grener styrt av is_user_logged_in() eller current_user_can() med mindre du har separate cache-varianter (Cookie, Cache-Control: private, bypass for wordpress_logged_in_*).
  2. Object cache (Redis/Memcached) er fin for has_term() og term-trær. Den erstatter ikke page-cache-disiplin.
  3. Fragment cache / ESI / client-side hydrate er alternativene når du trenger «rediger»-lenker på ellers cachet side.
  4. DONOTCACHEPAGE-konstanter og plugin-hooks finnes, men bruk dem smalt. En hel side satt til uncacheable fordi én widget sjekker kapasitet er en dyr løsning.

Testmatrise før lansering: anonym, abonnent, redaktør, på forside, single, kategoriarkiv. Hvis anonym og redaktør får samme HTML på en URL som skal vise ekstra UI, har du en cache-bug, ikke en conditional-bug.

#Unit-testbare hjelpere

Global state ($wp_query, $post, current user) gjør direkte kall til is_singular() vanskelig å teste. Pakk beslutningene:

final class Wppoland_Request_Context {
    public function __construct(
        private WP_Query $query,
    ) {}

    public function is_news_singular(): bool {
        if ( ! $this->query->is_singular( 'post' ) ) {
            return false;
        }
        $post_id = (int) $this->query->get_queried_object_id();
        return has_term( 'nyheter', 'category', $post_id );
    }
}

I WP_UnitTestCase (eller en integrasjonstest med Brain Monkey for isolasjon) bygger du en WP_Query, factory-poster med termer, og assert-er på hjelperen. Unngå å assert-e på HTML fra hele temaet når du egentlig tester én boolsk beslutning.

Retningslinjer:

  • Ta WP_Post|int og WP_Query som argumenter der det er mulig.
  • Returner bool eller små value objects, ikke echo.
  • Hold side effects (enqueue, header, redirect) i et tynt adapter-lag som kaller hjelperen.
  • Én helper, én grunn. wppoland_should_show_vat_notice( $post_id ) er lettere å navngi i en feilrapport enn et 40-linjers if-tre i functions.php.

#Ytelse i praksis

Én has_term() mot object cache er billig. Det som blir dyrt:

  • rekursive get_term_children() per rad i en stor admin-tabell,
  • N+1 der du resolver slug til term i en loop i stedet for å bruke ID,
  • conditional enqueue som treffer filemtime på disk for filer du ikke trenger.

Mål før du optimaliserer: Query Monitor på en single, et arkiv og forsiden. Hvis conditional-grenen ikke synes i slow queries, er lesbarhet viktigere enn mikrooptimalisering.

#Sjekkliste før du merger

  1. Er beslutningen makro (malhierarki / FSE-template) eller mikro (helper / hook)?
  2. Kjører sjekken etter at hovedspørringen er klar?
  3. Er post-ID eksplisitt når koden kan leve utenfor The Loop?
  4. Er brukergrener holdt utenfor anonym page cache?
  5. Finnes det en unit-test eller minst en WP_UnitTestCase for hjelperen?
  6. Bruker du has_term() (eller bevisst in_category()) konsistent?

#Oppsummering

Betinget logikk i WordPress er tre lag: query-flagg (is_singular, is_front_page, is_category), postdata (has_term, in_the_loop), og bruker (current_user_can). Malhierarki og FSE flytter hvor du skriver beslutningen, ikke om du trenger den. Cache straffer deg bare når du blander brukergrener inn i anonym HTML. Testbare hjelpere med eksplisitte argumenter er det som skiller en SOP fra enda en rotete functions.php.

Forskjellen på has_term(), is_category() og in_category() avgjør om en mal oppfører seg likt i arkivet og i enkeltinnlegget. Vi rydder gjerne opp i slik betinget logikk, se WordPress-utvikling for maler og taksonomier.

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.

Artikkel-FAQ

Ofte stilte spørsmål

Praktiske svar for å bruke temaet i faktisk arbeid.

SEO-readyGEO-readyAEO-ready3 Q&A
Når bør jeg bruke has_term i stedet for in_category?#
Bruk has_term når du vil støtte både kategorier og egendefinerte taksonomier. in_category fungerer bare for standardkategorier.
Hvordan sjekker jeg om et innlegg ligger i en barnekategori?#
Da trenger du ofte en hjelpefunksjon som bruker get_term_children, fordi standardfunksjonene ikke håndterer rekursive treff alene.
Kan betinget logikk påvirke ytelsen?#
Ja, særlig hvis du gjør mange dyre taksonomioppslag i store løkker. Object cache hjelper, men logikken bør fortsatt være målrettet.

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

Ta kontakt

Relaterte artikler