schedule5. august 2026 folderTutorial

Guide til WordPress-temaer

Nu har jeg lavet mine egne WordPress-temaer (templates) i henved 20 år, og har derfor oplevet hvordan både WordPress og temaer har udviklet sig gennem tiden. Det kan være en lidt uoverskuelig affære at vide hvilke filer og funktioner der gør hvad, og hvor de skal placeres, så jeg har samlet de vigtigste elementer i dette indlæg, og det kan måske hjælpe andre som vil give sig i kast med den ædle disciplin.

De medfølgende temaer har siden WP 5.0 i 2018 været såkaldte “block templates”, som består af kodeblokke der kombineres til et samlet design, som så kan tilrettes i blok-editoren. Det er sikkert smart nok til folk med begrænset kodekendskab, men i mine øjne virker det lidt uoverskueligt med en masse shortcodes og krydsreferencer mellem templates. Jeg foretrækker overskuelig kode, hvor jeg rent faktisk kan se hvad der foregår, så derfor omhandler guiden ikke block templates, men i stedet min måde at gøre det på. Det er overskueligt, simpelt, og det virker 🙂

Guiden forudsætter, at man har kendskab til HTML, CSS og lidt PHP, så det vil jeg ikke komme nærmere ind på. Men resten gennemgår jeg detaljeret, så de fleste forhåbentlig kan følge med.

Så smøg ærmerne op, og smid en kande kaffe på bålet. Here we go!

Filstruktur

Filstrukturen består af 3 mapper og en håndfuld filer, og jeg beskriver herunder, hvad de forskellige mapper skal indeholde, samt hvad .php-filernes formål er. Filerne i /assets/, /inc/ og /template-parts er ikke nødvendige, men det er blot for at illustrere, hvad mapperne kan indeholde. De filer vi gennemgår her, er alle dem der ligger i rodmappen.

mit-tema/

├── assets/
│ ├── css/
│ ├── js/
│ ├── images/
│ └── fonts/

├── inc/
│ ├── enqueue.php
│ ├── setup.php
│ ├── menus.php
│ ├── customizer.php
│ └── helpers.php

├── template-parts/
│ ├── hero.php
│ ├── card.php
│ ├── post.php
│ └── sections/

├── functions.php
├── header.php
├── footer.php
├── home.php
├── page.php
├── single.php
├── index.php
├── tag.php
├── category.php
├── search.php
├── archive.php
├── front-page.php
├── 404.php
└── style.css
Mappen /assets/indeholder alle de forskellige småbidder som får siden til at fungere – ekstra stylesheets, javascript-kode, billeder, skrifttyper osv – der kan selvfølgelig tilføjes flere mapper om nødvendigt.
Mappen /inc/Indeholder PHP-funktioner opdelt i hver deres fil, der så kan refereres til fra functions.php. Det være sig brugerdefineret sideinddelig, registrering af menuer, brugerdefineret resumé-længde osv.
mappen /template-parts/Indeholder brugerdefinerede skabeloner til designet, f.eks. sider, sidebars, kommentarvisning og post-formater.
404.phpFejlside “404 ikke fundet”
archive.phpVisning af arkivside
category.phpOversigt over en eller flere bestemte kategorier
footer.phpAfslutningen på siden
front-page.phpForside på websitet
functions.phpSamling af funktioner, “hooks” og filtre
header.phpStarten af hver side (HTML-struktur, titel osv.)
home.phpOversigt over indlæg
index.phpEn “catch-all” side, som bliver vist, hvis ingen andre skabeloner passer
page.phpStandardskabelon til sider
search.phpViser søgeresultater
single.phpEnkeltsidevisning af blogindlæg
style.cssStylesheet
tag.phpOversigt over et eller flere nøgleord (tags)

Det var en kort oversigt over hvad de forskellige filers funktion er. Lad os prøve at lave en helt simpel skabelon til at starte med, og så kan vi udvikle lidt på den hen ad vejen 🙂

Aktivering af temaet

For nemhedens skylds har jeg lavet et tomt tema med ovenstående skabeloner og et simpelt stylesheet, som vi kan downloade lige her og arbejde videre på. Vi starter med at uploade hele mappen mit-tema til vores WordPress-installation (under /wp-content/themes/). Nu kan vi logge ind i Admin og aktivere det under Udseende > Temaer. Her trykker vi Aktiver på “Mit Tema”.

Når vi nu kigger på vores hovedside, vil vi formentlig blive mødt af følgende:

Vi kan hermed konstatere, at temaet er korrekt installeret 🙂 Jeg vil lige hurtigt gennemgå diverse nødvendigheder i hhv. header.php, front-page.php og footer.php:

header.php
<!DOCTYPE html>
<html <?php language_attributes(); ?>>
    <head>
        <meta charset="<?php bloginfo('charset'); ?>">
        <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1, user-scalable=0">
        <title><?php wp_title(' | ', 'echo', 'right'); ?><?php bloginfo('name'); ?></title>
        <link rel="stylesheet" href="<?php echo get_bloginfo( 'stylesheet_directory' ); ?>/style.css?<?php echo md5(microtime()); ?>" type="text/css" media="screen">
        <?php wp_head(); ?>        
    </head>
    <body <?php body_class(); ?>>
PHP
  • language_attributes(); sætter sproget for siden, i vores tilfælde <html lang="da-DK">.
  • wp_title(); genererer dynamiske titler på siderne.
  • get_bloginfo( 'stylesheet_directory' ); er stien til vores stylesheet, og dermed resten af temafilerne.
  • md5(microtime()); genererer en tilfældig streng efter style.css ved hver sideindlæsning (jeg forklarer lige om lidt)
  • wp_head(); indsætter scripts og lignende, som er essentielle for WordPress. Det SKAL indsættes lige før det afsluttende </head> tag.
  • body_class(); indsætter diverse kontekst-betingede CSS-klasser i <body>.
front-page.php
<?php
/**
 * Template Name: Front Page
 */
get_header(); ?>

<main>
    <section>
        <div class="content">
        
        	FRONT-PAGE.PHP
        
        </div>
    </section>
</main>

<?php get_footer(); ?>
PHP
  • I begyndelsen af skabeloner er en sektion med /* ... Template Name: ... */, og det fortæller både WordPress at det er en skabelon, men også dens navn. Det bruges bl.a. når man opretter sider, hvor skabelonens navn så vil være der står her i koden. Praktisk, hvis man har flere forskellige skabeloner til diverse sider. Det kunne lige så vel være “Hjem”, “Velkommen” eller “Antikke Ølkapsler” – der er frit valg. Det er blot for at kunne kende forskel på skabelonerne.
  • get_header(); indlæser header.php som det første, og dermed hele HTML-strukturen. Denne linje SKAL være øverst i alle skabelonfiler.
  • get_footer(); indlæser footer.php. Denne linje skal være nederst i alle skabelonfiler.

Angående md5(microtime());: Browsere bruger en cache for at sikre hurtigere indlæsning af websider, men når det gælder CSS, kan det være en hindring, for laver man ændringer i sit stylesheet og derefter opdaterer sin side, bruger browseren ofte cache-versionen af stylesheet’et, så ændringerne ikke bliver vist. Ved at indsætte noget vilkårligt vrøvl efter styles.css, er det ifølge browseren en ny fil hver gang, som den så er tvunget til at genindlæse. Det er sgu meget smart 😉

Så – hele den lange <link rel...-kode bliver vist sådan her som HTML:

<link rel="stylesheet" href="https://[Wordpress]/wp-content/themes/Mit-Tema/style.css?ecdda02ad14579b2217b4c6f65eabd9c" type="text/css" media="screen">

Selve dokumentstrukturen er bygget sådan op, at forskellige filer (“blokke”) inkluderer og/eller er afhængige af hinanden. Jeg ved godt, at jeg i indledningen sagde jeg ikke brød mig om “block templates”, og nu er vi ved at lave én…? 🙂 Bare rolig, denne metode er MEGET mere simpel og letlæselig end at begynde at lave undertemaer til de medfølgende temaer, eller forsøge at tilrette et af dem.

Navigation

På nuværende tidspunkt har vi kun en forside, så vi skal have lavet en side til at vise blogindlæg også … og det gøres smartest ved at starte med en navigationsmenu i headeren, så vi nemt kan skifte mellem sider.

Vores tema understøtter dog ikke menuer endnu, så det skal vi have sat op i functions.php. Der er en funktion i filen i forvejen (til at fjerne adminbjælken øverst, når man er logget ind), så tilføj denne under den (alle funktioner skal være imellem <?php og ?>):

functions.php
<?php

/* Hide admin bar (called from footer) */
add_filter( 'show_admin_bar', '__return_false' );

/* Register nav menus */
function nav_menus() {
    register_nav_menus([
        'header' => 'Header Menu',
        'sidebar' => 'Sidebar Menu'
    ]);
}
add_action( 'after_setup_theme', 'nav_menus', 0 );

?>
PHP

Vi har nu lavet vores første funktion, som registrerer navigationsmenuer. Der kan laves så mange menuer man lyster, og hver af dem kan indeholde forskellige sider og links. Her har vi oprettet to menuer, én til headeren 'header' => 'Header Menu', og én til en sidebar 'sidebar' => 'Sidebar Menu'. Hvis man vil have flere eller slette nogen, justerer man bare i listen.

Vi skal også lige have placeret menuen i headeren, så lad os åbne header.php og tilføje wp_nav_menu(); i <header>-elementet:

header.php
<header>
  <?php wp_nav_menu(); ?>   
</header>
PHP

Når vi nu går ind i Admin > Udseende, har vi et nyt menupunkt der hedder Menuer, og her kan vi se vores to menuer. Skriv et navn i “Menunavn”, vælg om det skal være header/sidebar, og tryk “Opret menu”. Vi kan derefter tilføje sider, indlæg, brugerdefinerede links og kategorier fra boksen til venstre. Hvis det er en ny WordPress-installation, er der sikkert kun Eksempelside, så det må vi lave om på.

Oprettelse af sider

I menuen vil vi nok gerne have et link til forsiden, et link til bloggen og et link til en standardside. Det gøres under Sider og derefter “Tilføj side”. Nu kan vi lave en overskrift, f.eks. “Min Blog”, og så vælger vi skabelonen Home (som jo er vores indlægsoversigt). Derefter klikker vi “Udgiv” to gange, og så er dén side på plads.

Nu laver vi forsiden på samme måde, men skriver bare “Forside” eller noget i titlen, og vælger Front Page som skabelon. Og til sidst laver vi en standardside, som vi kalder “Om Plat & Svindel ApS”, og vælger skabelonen Page til den.

Nu har vi så tre sider, så tilbage i Menuer markerer vi dem og trykker “Tilføj til menu”. Herefter kan de trækkes rundt for at ændre rækkefølgen, og hvis man har mange sider kan der laves undermenuer også. Husk at trykke “Gem menu” til sidst.

Nu har vi så vores menu i headeren. Prøv at klikke på “Om Plat & Svindel ApS” og “Min blog” – de skulle gerne vise hhv. PAGE.PHP og HOME.PHP lige under headeren.

NU er vi klar til at hente vores blogindlæg! Jeg ved godt det har været kringlet indtil videre, men jeg ville starte helt fra begyndelsen, for at vi kan få en bedre forståelse for hvordan et tema er struktureret – det er en klar fordel for os senere.

the_loop();

WordPress henter stort set alt indhold med funktionen the_loop(); – en oversigt med blogindlæg, en enkeltsidevisning, arkiver, sider osv. Det fungerer groft sagt sådan her:

  1. hvad er konteksten? Er vi på en side med blogindlæg, en side der viser et enkelt indlæg, en arkivside der viser kategori osv.
  2. lav en query, dvs. forespørgsel til databasen baseret på hvad konteksten er
  3. hvis vi har indhold/indlæg (posts), så …
  4. kør en løkke så længe vi har posts og vis indlæg fortløbende
  5. vis HTML-outputtet af disse indlæg, til der ikke er flere resultater i query’en
  6. afslut løkken. Hvis ingen indlæg blev fundet, så …
  7. giv brugeren en meddelelse: “Ingen resultater fundet”

Lad os prøve at indsætte følgende kode i home.php:

home.php
<?php
/**
 * Template Name: Home
 */
get_header(); ?>

<main>
    <section>
        <div class="content">
        	<div class="articles">

<?php if ( have_posts() ) : // hvis vi har indhold
    while ( have_posts() ) : the_post(); // kør en løkke og vis posts fortløbende ?>
    
        <!-- start på output -->
        <article>
        <?php the_title(); // titel ?>
        <?php the_excerpt(); // indhold ?>
        </article>
        <!--slut på output -->
        
    <?php endwhile; else : // afslut løkken. hvis intet fundet ... ?>
	<?php echo 'Ingen resultater fundet...'; // giv brugeren en meddelelse ?>
	<?php endif; ?>
			
			</div>

			<div class="sidebar-container"></div>

    </section>
</main>

<?php get_footer(); ?>
PHP

Al vores html-formaterede output skal være imellem while ( have_posts() ) : the_post(); og endwhile; else :. Funktionen the_title(); viser titlen på indlægget, og the_content(); indholdet. Prøv at klikke på “Min blog” i menuen – siden burde nu vise blogindlæg, men i stedet viser den bare “Min blog”, ikke sandt?

Det skyldes, at vi ikke har fortalt WordPress, hvilken side der er ansvarlig for at vise indlæg. Gå til Admin > Indstillinger > Læsning. Her kan vi vælge, om vores forside skal vise seneste indlæg eller en statisk side. Sæt Forside til “Forside” og Indlægsside til “Min blog” og gem ændringerne.

Nu får vi vist vores indlæg med home.php (Min blog), men det er HELE indlæg – tekstformatering, billeder og alt muligt andet i én pærevælling. Det er jo noget rod. Vi vil hellere have en simpel og overskuelig liste over vores indlæg. Derfor kan vi erstatte the_content(); med the_excerpt();, som i stedet viser uddrag af indlæggene uden formatering:

home.php
<article>
<?php the_title(); // titel ?>
<?php the_excerpt(); // uddrag ?>
</article>
PHP

Så langt, så godt. Men man skal jo også kunne læse indlæggene, så vi skal linke hver titel til dens respektive indlæg – og det er her funktionen the_permalink(); kommer ind i billedet. Den giver et direkte link til indlægget, så det er simpelthen bare at pakke the_title(); ind i et link der refererer til the_permalink();:

home.php
<article>
<a href="<?php the_permalink(); ?>"><?php the_title(); ?></a>
<?php the_excerpt(); // indhold ?>
</article>
PHP

Sideinddeling (paginering)

“Men hov!”, kan jeg høre dig sige. “Jeg er en rigtig flittig blogger med hundredevis af indlæg. Jeg kan kun se 10 på siden?”. Det er korrekt – som vi så i Indstillinger > Læsning, viser WordPress 10 indlæg pr. side, og dette bør ikke under nogen omstændigheder ændres! At vi ikke kan skifte side, skyldes at vi ikke har implementeret paginering endnu.

Dette gøres med funktionen the_posts_pagination();, som giver links til sider. Sæt det ind i home.php, lige efter endif;:

home.php
<?php endif; ?>
<?php the_posts_pagination(); ?>
PHP

Enkeltsidevisning

Nu har vi vores indlægsoversigt klar, så lad os kigge lidt på enkeltsidevisningen af indlæg. Det er single.php som står for det, og også her bruger vi the_loop(); til at hente informationer. Åbn single.php og tilføj følgende kode:

single.php
<?php
/**
 * Template Name: Single
 */
get_header(); ?>

<main>
    <section>

<?php if ( have_posts() ) :

    while ( have_posts() ) : the_post(); ?>

        <div class="content-single">
        <?php the_title();?>
        <?php the_content(); ?>
        </div>

    <?php endwhile; else : ?>
<?php echo 'Ingen resultater fundet...'; ?>

<?php endif; ?>

    </section>
</main>

<?php get_footer(); ?>
PHP

Nu henter vi igen titlen på indlægget (the_title();), samt alt indholdet med the_content();. Og ligesom med indlægssiden er det pakket ind i HTML-elementer for at positionere tingene korrekt.

Men foruden titel og indhold, kan vi også vise andre informationer om indlægget – tidspunkt, kategori, tags osv., så sad os prøve at flette det ind. Tilføj følgende til single.php lige over the_title();:

single.php
<div class="content-single">
<div>Indlægsdato: <?php the_date(); ?></div>
<div>Klokken <?php the_time(); ?></div>
<div>Kategori: <?php the_category(); ?></div>
<div><?php the_tags(); ?></div>
<?php the_title();?>
<?php the_content(); ?>
</div>
PHP

Som det nok kan anes, henter the_* datoen og tidspunkt for indlægget, hvilke(n) kategori(er) det er arkiveret under, samt indlæggets nøgleord (tags):

Arkivsider (kategori)

Når vi klikker på et kategori-link, føres vi til en indlægsoversigt med den specifikke kategori. For at lave denne arkivside, er det category.php vi skal bruge. Den fungerer ligesom home.php med the_loop;, men fordi konteksten nu er arkiv, bliver kun den aktuelle kategori som er i URL’en vist – i dette tilfælde “kaffe”:

Tilføj følgende kode i category.php:

category.php
<?php
/**
 * Template Name: Category
 */
get_header(); ?>

<main>
    <section>
        <div class="content">
        	<div class="articles">

<?php if ( have_posts() ) : // hvis vi har indhold
    while ( have_posts() ) : the_post(); // kør en løkke og vis posts fortløbende ?>

        <article>
        <a href="<?php the_permalink(); ?>"><?php the_title(); ?></a>
        <?php the_excerpt(); ?>
        Udgivet: <?php echo get_the_date('j. F Y'); ?>
        Klokken <?php the_time(); ?><br />
        Kategori: <?php the_category(); ?><br />
        <?php the_tags(); ?>
        </article>

    <?php endwhile; else : // afslut løkken. hvis intet fundet ... ?>
	<?php echo 'Ingen resultater fundet...'; // giv brugeren en meddelelse ?>
	<?php endif; ?>
	<?php the_posts_pagination(); ?>
			
			</div>

			<div class="sidebar-container"></div>

    </section>
</main>

<?php get_footer(); ?>
PHP

Her bruger vi samme struktur som i home.php, og vi har tilføjet visning af indlæggenes kategorier og tags med hhv. the_category(); og the_tags();. Vi viser også datoen for indlæggets udgivelse, men i modsætning til the_date(); fra single.php, bruger vi her echo get_the_date();.

Derfor bruger vi altså echo get_the_date(); i denne sammenhæng for at få dato på samtlige indlæg – også dem som er udgivet samme dag.

Der kan tilføjes parametre til datofunktionerne, og disse følger PHPs datoformater. I koden herover bruger vi 'j. F Y', som vil give f.eks. “23. august 2025”. Bruger vi 'm.d.y', giver det “23.08.25” og så fremdeles. Nu ser kategorioversigten altså således ud:

Prøv at klikke lidt frem og tilbage på de forskellige kategorier, og se hvordan indlæggene ændres – og vi bruger kun én og samme skabelon.

Arkivsider (tags)

For at lave en arkivside til tags, kan vi simpelthen bare copy/paste fra category.php – husk dog at ændre Template Name til “Tag” i stedet:

PHP
<?php
/**
 * Template Name: Tag
 */
get_header(); ?>

<main>
    <section>
        <div class="content">
        	<div class="articles">

<?php if ( have_posts() ) : // hvis vi har indhold
    while ( have_posts() ) : the_post(); // kør en løkke og vis posts fortløbende ?>

        <article>
        <a href="<?php the_permalink(); ?>"><?php the_title(); ?></a>
        <?php the_excerpt(); ?>
        Udgivet: <?php echo get_the_date('m.d.y'); ?>
        Klokken <?php the_time(); ?><br />
        Kategori: <?php the_category(); ?><br />
        <?php the_tags(); ?>
        </article>

    <?php endwhile; else : // afslut løkken. hvis intet fundet ... ?>
	<?php echo 'Ingen resultater fundet...'; // giv brugeren en meddelelse ?>
	<?php endif; ?>
	<?php the_posts_pagination(); ?>
			
			</div>

			<div class="sidebar-container"></div>

    </section>
</main>

<?php get_footer(); ?>
PHP

Det vil give nøjagtigt samme output som kategorivisningen, og nu kan vi klikke lidt rundt imellem tags for at få vist indlæg, som specifikt er mærket med disse.

Fallback til archive.php og hierarki

Foruden category.php og tag.php kan der tilføjes flere skabeloner til forskellige arkivvisninger, f.eks. author.php, date.php osv., som viser indlæg fra en bestemt forfatter, fra en bestemt dato og så videre og så videre… det er omfattende 🙂 Derfor beskæftiger vi os kun med kategorier og tags i denne guide.

Hvis en af disse skabeloner ikke findes, og man alligevel forsøger at få vist en arkivside med den pågældende, vil WordPress lave “fallback” til archive.php. Det vil sige, at selvom den specifikke skabelon ikke findes, vil indholdet – det være sig forfatter, dato el.lign. – alligevel blive “fanget” af archive.php og vist dér i stedet (forudsat at the_loop(); er til stede på den side).

Der er nemlig et hierarki med primære, sekundære og variable skabeloner, og hvis en sekundær skabelon ikke kan findes, vil WordPress bruge den primære. På WordPress Developer Resources kan man læse meget mere om det. Men i grove træk er de grønne skabeloner sekundære (kan undværes), mens de lilla er primære (påkrævede).

Lad os tage et eksempel på hierarki: Vi har ingen date.php-skabelon. Vi har 3 indlæg, alle tilføjet 2. august 2022 – “Solen skinner, og det er godt”, “Er der mere kaffe?” og “Kattebakken skal tømmes”. Deres permalinks (URL) er:

/wordpress/2022/08/02/solen-skinner-og-det-er-godt/
/wordpress/2022/08/02/er-der-mere-kaffe/
/wordpress/2022/08/02/kattebakken-skal-toemmes/

Hvis vi nu sletter titlerne, og prøver at tilgå kun datoerne:

/wordpress/2022/08/02/

… så vil WordPress først prøve at bruge skabelonen date.php (sekundær). Hvis den ikke findes, vil siden archive.php (primær) vises i stedet. Hvis the loop(); er til stede dér, er der ingen ko på isen – vi får en liste over disse tre indlæg med samme dato. Hvis the_loop(); IKKE er til stede, får vi intet output – vi får bare vist hvad archive.php nu engang indeholder.

Med andre ord kan man godt undlade at have category.php, tag.php, date.php osv. i sin installation, og så lade archive.php klare alle arkivvisninger med if- eller switch-statements.

Det kan dog hurtigt blive en lang og omfattende fil, så derfor foretrækker jeg for det meste at have en fil til hvert specifikt formål – og det er altid godt at have noget at falde tilbage på 😉

Så kort og godt: archive.php er en nødvendig fil for WordPress, men bruger man de andre filer til arkivsider, kan den bare efterlades blank.

Søgning

Vi skal selvfølgelig også have en søgefunktion i vores tema, og også dét bliver klaret af en funktion, get_search_form();. Den laver et simpelt søgefelt, og kan f.eks være i en sidebar, i headeren eller hvor man end lyster på siden. Vi kan prøve at placere den i headeren, så tilføj følgende i header.php:

header.php
<header>
	<?php wp_nav_menu(); ?>
  <?php get_search_form(); ?>
</header>
PHP

Nu har vi et lille søgefelt under menuen – nifty 😉 Der bruges også en separat skabelon til at vise en liste med søgeresultater, nemlig search.php. Ligesom alle de andre gør den også brug af the_loop();, så lad os få lavet den:

search.php
<?php
/**
 * Template Name: Search
 */
get_header(); ?>

<main>
    <section>
        <div class="content">
        	<div class="articles">
        	<h1>Søgeresultater</h1>
            <h2>Du søgte efter: <?php echo $s; ?></h2>

<?php if ( have_posts() ) : // hvis vi har indhold
    while ( have_posts() ) : the_post(); // kør en løkke og vis posts fortløbende ?>

        <article>
        <a href="<?php the_permalink(); ?>"><?php the_title(); ?></a>
        Udgivet: <?php echo get_the_date('m.d.y'); ?>
        Klokken <?php the_time(); ?><br />
        Kategori: <?php the_category(); ?><br />
        </article>

    <?php endwhile; else : // afslut løkken. hvis intet fundet ... ?>
	<?php echo 'Ingen resultater fundet...'; // giv brugeren en meddelelse ?>
	<?php endif; ?>
	<?php the_posts_pagination(); ?>
			
			</div>

			<div class="sidebar-container"></div>

    </section>
</main>

<?php get_footer(); ?>
PHP

Den kan selvfølgelig udformes som man vil, men lad os blive i den vante stil og nærmest bare lave den som de to foregående. Dog er der en lille undtagelse – vi viser ikke uddrag og tags i denne liste, simpelthen bare for at holde den enkel. Til gengæld har vi en overskrift, Søgeresultater, samt hvad man har søgt efter på linjen under. Det er variablen $s som viser det.

Sidebar

Man kan have sidebars på siderne, som viser forskellige ting som arkiver, dagens menu i kantinen, vejrudsigten, kattebilleder eller whatever. For at tilføje en sådan, bruges funktionen get_sidebar();. Hvis man har filen sidebar.php i installationen, vil get_sidebar(); vise indholdet af den. Hvis ikke, vil funktionen vise en standardoversigt over Sider, Arkiver, Kategorier og Meta (hvor man kan logge ind/ud og tilgå Admin). Lad os prøve at tilføje følgende til home.php:

home.php
<div class="sidebar-container">
<?php get_sidebar(); ?>
</div>
PHP

Div’en sidebar-container er for at holde sidebaren på plads ude til højre for indlægsoversigten, og vi kalder sidebaren frem inde i den. Nu har vi følgende på siden:

Den er i og for sig fin nok, og passer sikkert til rigtig mange, men den er en lille smule … hmm, “WordPress-y”, som man siger på godt dansk – altså lidt for standard 🙂 Jeg synes det er mere interessant at lave og style sin egen, så man skiller sig lidt ud.

Man kan som sagt oprette sidebar.php og tilpasse sin egen dér, og man kan endda have forskellige sidebars, alt efter hvilken side man er på osv (mere info her). Men nu er det jo sådan, at sidebaren er en del af skabelonen, så derfor foretrækker jeg at have den i mappen /template-parts/. Opret minsidebar.php dér, og – tja, du kan bare skrive lige hvad der passer dig i den:

/template-parts/minsidebar.php
<div id="sidebar">
<img src="https://placekittens.com/200/300" />
<ul>
	<li><h2>Min custom sidebar</h2></li>
	<li>Kan tilpasses med hvad man nu synes der skal være i en sidebar.</li>
	<li>Dagens joke, vejrudsigten, seneste kommentarer etc.</li>
</ul>
</div>
PHP

MEN! Hvis vi henter sidebaren fra et andet sted end rodmappen, eller vi inkluderer andre skabelon-elementer – skal det gøres på en lidt anden måde, nemlig med get_template_part();. Så erstat get_sidebar(); med get_template(); i home.php:

home.php
<div class="sidebar-container">
	<?php get_template_part('template-parts/minsidebar'); ?>
</div>
PHP

Parameteren fortæller funktionen hvor vores egen sidebar er placeret – og læg mærke til, at vi ikke skal have en endelse på “minsidebar”. WordPress véd pr. automatik at det er en .php-fil.

Pspspspspsps… 😉

Nu tror jeg vi har været igennem alle relevante skabeloner, og har fået de grundlæggende funktioner på plads. Som sagt i starten går jeg ud fra, at man har kendskab til HTML, PHP og CSS, så jeg vil ikke vise hvordan man udformer de forskellige elementer. WordPress indsætter automatisk CSS-klasser på langt de fleste elementer, som man kan style ud fra, og man kan med parametre også indsætte sine egne klasser. Mulighederne er uendelige 🙂

Custom Queries I

Foruden den almindelige indlægsvisning, som viser alle posts i alle kategorier og med 10 pr. side, kan man tilpasse sin query (custom query), så bestemte kategorier udelades / så søgning kun returnerer posts, og ikke sider / at siderne viser x indlæg pr. side, og meget, meget mere.

Jeg nævnte godt nok under afsnittet “Sideinddeling”, at man absolut ikke bør ændre, at WordPress viser 10 indlæg pr. side, og det holder stadig stik.

<nørd>
Det mere tekniske aspekt i det er, at WordPress allerede har en hovedforespørgsel (main query), som bestemmer hvilke indlæg der skal vises på siden, samt hvor mange sider med indlæg der findes. Hvis man laver en custom query, laver man i praksis en ekstra forespørgsel ved siden af WordPress’ egen. Så ved WordPress ikke automatisk, at den ekstra forespørgsel skal bruges til f.eks. links til sidetal eller forrige/næste side, da den stadig beregner dette ud fra main query.
</nørd>

Det kan give problemer med sideinddelingen – f.eks. at der vises et forkert antal sider, at alle indeholder det samme, eller at alle sider på nær den første giver “404 ikke fundet”-fejl. Derfor er det altid bedst at modificere main query i stedet for at lave en custom query, og det er det vi vil gøre her.

Det gøres i functions.php, så åbn den og tilføj følgende pre_get_posts(); funktion:

functions.php
/* Custom Queries */
add_action('pre_get_posts', function ($query) {

    if (is_admin() || !$query->is_main_query()) {
        return;
    }

    // If the page is home.php
    if ($query->is_home()) {
        $query->set('category__not_in', [1, 2, 3, 8]);  // exclude categories
        $query->set('posts_per_page', 10);              // 10 posts per page
    }

    // If the page is archive.php
    if ($query->is_archive()) {
        $query->set('cat', 4);            // only show posts from this category ID
        $query->set('posts_per_page', 3); // 3 posts per page
    }

    // If the page is search.php
    if ($query->is_search()) {
        $query->set('post_type', 'post'); // only show posts
        $query->set('posts_per_page', 5); // 5 posts per page
    }

});
PHP

Det er lidt af en mundfuld, og der er tre betingelser i samme funktion, men oversat til dansk gør den følgende:

  1. hvis siden er home.php (if $query->is_home();), dvs. vores indlægsoversigt, så ekskludér følgende kategori-ID’er: 1, 2, 3, 8 ($query->set('category__not_in', [1, 2, 3, 8]);) og vis 10 indlæg pr. side ($query->set('posts_per_page', 10);)
  2. hvis siden er archive.php (if $query->is_archive();), dvs. en arkivside, så vælg kun kategorien med ID 4 ($query->set('cat', 4);), og vis 3 indlæg pr. side ($query->set('posts_per_page', 3);)
  3. hvis siden er search.php (if $query->is_home();), dvs. søgeresultater, så vis kun posts (dvs. udelukkende indlæg, og ikke sider eller andet) ($query->set('post_type', 'post');) og vis 5 indlæg pr. side ($query->set('posts_per_page', 5);)

Den øverste betingelse skal også inkluderes, og betyder kort og godt “Hvis vi er i WordPress’ administrationsområde, eller hvis denne forespørgsel ikke er main query, så stop her – ellers kør funktionen”.

I øvrigt … den nemmeste måde at finde en kategori-ID på, er at holde musen over den i Admin > Indlæg > Kategorier, og se efter tag_ID i statusbjælken:

Custom Queries II

Der er dog en undtagelse hvad angår forrige sektion. Hvis man vil bruge custom queries på en enkelt side, dvs. hvor man bare vil vise én side med præsentationer af sine 17 antikke ølkapsler, behøver man ikke functions.php til at tilpasse forespørgslen.

Lad os forestille os, at vi laver en side “Min samling af ølkapsler” med page-kapsler.php som skabelon:

Derefter opretter vi 17 indlæg – ét for hver kapsel – med titel, brødtekst og et billede, og gemmer dem i kategorien “Kapsler”. Hvis vi på denne side så vil have vist titlen, indledende tekst, og derefter de 17 indlæg, gøres det således:

page-kapsler.php
<?php
    /**
     * Template Name: Kapsel-skabelon
     */
    get_header(); ?>
    
<?php
// lav en custom query
$args = array(
  'post_type'         => 'post',
  'post_status'       => 'publish',
  'category_name'     => 'kapsler',
  'posts_per_page'    => -1
); ?>

<?php echo the_title(); // sidens overskrift ?>
<?php echo the_content(); // sidens brødtekst ?>

<?php
$myQuery = new WP_Query($args); // opret objekt

if ( $myQuery->have_posts() ) :
    while ( $myQuery->have_posts() ) : $myQuery->the_post(); ?>

        <article>
          <?php the_title(); ?>
          <?php the_content(); ?>
        </article>

    <?php endwhile; else : ?>
	<?php echo 'Ingen resultater fundet...'; ?>
	<?php endif; ?>

<?php get_footer(); ?>
PHP

Vi starter med at lave query’en på nogenlunde samme måde som i functions.php, men her bruger vi i stedet et array, som indeholder parametrene ($args), Da en query er et objekt, har det properties og methods (key=>value), hvor properties er key (‘post_type’, ‘category_name’ osv.), og methods er så value, som vi selv definerer.

I eksemplet her vil vi kun have vist typen “indlæg” ('post_type' => 'post'), som er udgivet ('post_status' => 'publish'). Kategorien skal være “Kapsler” (‘category_name' => 'kapsler'), og vi vil have vist alle forekomster ('posts_per_page' => -1). “-1” betyder “alle”, og hvis vi ikke har den parameter med, vil WordPress bryde ind med sin main query, og kun vise sine sædvanlige 10 indlæg.

Derefter viser vi sidens titel og brødtekst med the_title(); og the_content();.

Så opretter vi queryen som et nyt objekt ($myQuery;) med new WP_Query($args);.

Når vi så henter indlæggene, skal the_loop(); ændres lidt, så den bruger vores egen query. Det gøres ganske enkelt ved at tilføje $myQuery-> foran have_posts() og the_post(). Det vil liste alle vores forekomster af kapsler pakket ind i <article>-tags.

Lidt mere om functions.php

I denne fil ligger funktioner – såkaldte “filtre” og “hooks” – som ændrer den måde WordPress agerer. Er man ikke tilfreds med den måde kommentarer er designet kode- og layoutmæssigt, eller vil man ændre teksten i søgeknappen, foregår det i functions.php. Jeg har i mange år haft samtlige af mine funktioner samlet i netop denne fil, men det blev et virvar til sidst, så en mere simpel måde er at have dem i mappen /inc/ eller andetsteds, og så lave henvisninger til dem:

functions.php
require get_template_directory() . '/inc/custom-excerpt-length.php';
require get_template_directory() . '/inc/min-anden-funktion.php';
require get_template_directory() . '/template-parts/noget-template-halløj.php';
require get_template_directory() . '/inc/en-helt-tredje-funktion.php';
PHP

I nedenstående eksempel henviser vi til et filter, som ændrer standard-uddragslængden. get_template_directory() angiver stien til vores temamappe, og tilføjer så undermappen /inc/ (f.eks. mit-tema/inc/custom-excerpt.php). Selve funktionen i custom-excerpt.php ser således ud:

custom-excerpt.php
/* Custom Excerpt Length */
add_filter( 'excerpt_length', function($length) {
    return 30;
}, PHP_INT_MAX );
PHP

Hooks

Jeg er langtfra ekspert på dette område, men helt firkantet er excerpt_length én af mange funktioner i WordPress-kernen, hvor man kan “hægte sig på / hook on to”, og ændre eksekveringen og funktionaliteten af koden. Dvs. hvis man bruger the_excerpt(); for at få vist et uddrag af en artikel, er det funktionen excerpt_length, som spytter 55 ord ud og afslutter med […] (eller noget i den stil). Her bryder vi ind i dette hook, og får the_excerpt(); til kun at returnere 30 ord i stedet.

Et “hook” er altså grundlæggende en slags “Hey! Stop lige, og gør det her i stedet”, og der er to typer:

Action hooks – add_action() – som udfører noget på bestemte tidspunkter, f.eks. at sende en mail, efter et indlæg er offentliggjort, eller registrere navigationsmenuer.

Filter hooks – add_filter() – som modificerer data, f.eks. ændrer en titel eller værdi inden den bliver vist.

Er vi stadig med? 😁 Godt! Det er en større videnskab at mestre WordPress til fulde, men jeg var nødt til lige at forklare det tekniske aspekt. Heldigvis kan man finde alverdens funktioner på nettet, så der er ingen grund til selv at opfinde den dybe tallerken, hvis man lige vil have ændret nogle småting.

Genbrug listevisningen

Nu hvor vores kategori-, tag- og søgeside stort set er identiske mht. hvordan indlægslisten bliver vist, er der faktisk en nemmere måde at gøre det på, så man ikke behøver ændre i tre forskellige filer, hvis man vil have samtlige sider til at formatere outputtet på en anden måde.

Her gør vi igen brug af get_template_part(); som til sidebaren, men denne gang placerer vi den i the_loop();. Lad os prøve at oprette filen showposts.php i mappen /template-parts/:

/template-parts/showposts.php
<article>
  <a href="<?php the_permalink(); ?>"><?php the_title(); ?></a>
  Udgivet: <?php echo get_the_date('m.d.y'); ?>
  Klokken <?php the_time(); ?><br />
  Kategori: <?php the_category(); ?>
  <?php the_excerpt(); ?>
</article>
PHP

Dette er den kodeblok, som pakker hvert indlæg ind i <article>, og giver os titel, udgivelsestidspunkt, kategori og uddrag. Hvis vi nu åbner category.php, tag.php og search.php, kan vi ændre lidt i dem:

category.php | tag.php | search.php
<?php if ( have_posts() ) : 
  while ( have_posts() ) : the_post(); ?>
  
    <?php get_template_part('template-parts/showposts'); ?>
    
  <?php endwhile; else : ?>
  <?php esc_html_e( 'Ingen resultater fundet' ); ?></div>
<?php endif; ?>
PHP

Vi erstatter simpelthen bare vores tidligere kode i the_loop(); med get_template_part();, som fletter indholdet fra /template-parts/showposts.php ind i filen. På den måde kan man nøjes med at lave rettelser i én fil, og så bliver det reflekteret i alle dem, som henviser til den. Og ligesom med sidebaren, skal vi ikke have endelsen med i filnavnet.

index.php

Her på falderebet kan vi kaste et blik på index.php. Ligesom archive.php er det en “fallback”-fil, og er den sidste i hierarkiet som bliver vist, hvis alle dens “overordnede” specifikke skabeloner mangler. Dens primære formål er altså at sikre, at WordPress altid har en side at vise, hvis alt andet fejler. Men akkurat som med archive.php – hvis alle andre filer længere oppe i hierarkiet er til stede og i orden, behøver index.php ikke at indeholde noget. Men den er nødvendig, for at temaet kan fungere.

404 Not Found

Lige til allersidst har vi 404.php. Det er ganske enkelt en fejlside, som bliver vist, hvis en side (URL) ikke findes:

Vi kan vælge at sætte den op som vores andre skabeloner, så den passer ind i temaet, eller også kan vi lave den som en selvstændig side med eget stylesheet og indhold. Jeg har f.eks. et lille Amiga-flip kørende her på Psylicium.dk 😉 http://psylicium.dk/404

The End

Det var så det – en meget lang smøre, og du har sikkert ikke mere kaffe tilbage. WordPress har udviklet sig fra simpel blogplatform i 2003 til et fuldt udviklet professionelt CMS den dag i dag. Jeg har brugt det siden 2006, og kan slet ikke forestille mig at skulle undvære det. Jeg elsker at give hjemmesiden en ansigtsløftning fra tid til anden, og bliver til stadighed overrasket over, hvor fleksibelt og genialt det egentlig er.

Derfor lavede jeg denne lange, men omfattende guide i håbet om at andre kunne få blod på tanden, eller måske få opklaret nogle spørgsmål jeg indirekte har besvaret her.

God fornøjelse med WordPress 🙂

:(
Ingen kommentarer

Hej med dig! Har du noget at tilføje?

Din e-mailadresse vil ikke blive publiceret. Krævede felter er markeret med *

Dette site anvender Akismet til at reducere spam. Læs om hvordan din kommentar bliver behandlet.