Feltet Repeater i Advanced Custom Fields (ACF Pro) er en hjørnestein i moderne WordPress-utvikling. Det gir innholdsforvaltere full fleksibilitet til å administrere lister med modulære elementer, som teamoversikter, prisplaner, arrangementstidslinjer eller kundeanbefalinger, uten å måtte opprette egne tilpassede innleggstyper (Custom Post Types) for hvert minste delinnhold.
Når designere utarbeider skreddersydde layouter, oppstår det nesten alltid et behov for differensiert visning. Typiske eksempler er sikksakk-oppsett der bilde og tekst bytter plass for hver rad, alternerende bakgrunnsfarger (“sebra-striper”), eller spesielle visuelle uttrykk for den første eller siste modulen i rekken.
I denne veiledningen går vi grundig gjennom hvordan du implementerer dette teknisk i malfilene dine, hvordan ACF lagrer data i databasen, og hvordan du trekker en fornuftig grense mellom PHP-styrt logikk og moderne CSS-egenskaper.
Hvordan ACF Repeater lagres i WordPress-databasen
For å skrive ren og robust kode må utviklere forstå hvordan WordPress og ACF organiserer gjentakende data under panseret. WordPress lagrer metadata i tabellen wp_postmeta som enkle nøkkel-verdi-par.
Når du oppretter et repeater-felt med navnet tjeneste_elementer som inneholder to underfelt (tittel og beskrivelse), og redaktøren legger inn fire rader, genererer ACF følgende oppføringer i databasen:
- En tellerrad:
meta_key = 'tjeneste_elementer'medmeta_value = '4'. - Metadata for rad 0:
tjeneste_elementer_0_tittelogtjeneste_elementer_0_beskrivelse. - Metadata for rad 1:
tjeneste_elementer_1_tittelogtjeneste_elementer_1_beskrivelse. - …samt systemrader med feltnøkler (
_tjeneste_elementer_0_tittel=field_65a12b8).
For 10 rader med 4 underfelt betyr dette over 80 rader i wp_postmeta for ett enkelt innlegg. Dette understreker hvorfor ukritisk bruk av uoptimaliserte spørringer i maler kan svekke sideinnlasting og responsivitet på travle nettsteder.
Hvordan bruke have_rows og the_row i ACF Repeater
Den offisielle og tryggeste måten å iterere gjennom repeater-felter på, er å kombinere funksjonene have_rows() og the_row():
<?php if ( have_rows('tjeneste_elementer') ) : ?>
<section class="tjenester-repeater-wrapper" aria-label="<?php esc_attr_e('Våre tjenester', 'wppoland'); ?>">
<div class="tjenester-liste">
<?php while ( have_rows('tjeneste_elementer') ) : the_row();
$tittel = get_sub_field('tittel');
$tekst = get_sub_field('beskrivelse');
$bilde = get_sub_field('illustrasjon');
?>
<article class="tjeneste-kort">
<?php if ( ! empty( $bilde ) && is_array( $bilde ) ) : ?>
<div class="tjeneste-bilde">
<?php echo wp_get_attachment_image( (int) $bilde['id'], 'medium_large' ); ?>
</div>
<?php endif; ?>
<div class="tjeneste-tekst">
<h3><?php echo esc_html( $tittel ); ?></h3>
<div class="innhold"><?php echo wp_kses_post( $tekst ); ?></div>
</div>
</article>
<?php endwhile; ?>
</div>
</section>
<?php endif; ?>Funksjonen the_row() flytter den interne markøren frem og etablerer gjeldende kontekst. Inne i løkken må du alltid kalle get_sub_field() i stedet for get_field(), da sistnevnte vil hente felt fra innleggets globale nivå.
Hvordan style ACF-repeater-rader med get_row_index og modulo
Når designet krever at annenhver rad skal ha en annen bakgrunnsfarge, omvendt kolonnerekkefølge eller distinkte klasser, bruker vi kontrollogikk i PHP.
Hvordan get_row_index fungerer i ACF
I stedet for å definere en manuell teller $i = 0 før løkken, har ACF en innebygd funksjon kalt get_row_index(). Den returnerer posisjonen til den aktive raden som en 1-basert indeks (første rad er 1, andre rad er 2 osv.):
<?php if ( have_rows('prosess_trinn') ) : ?>
<div class="prosess-trinn-beholder">
<?php while ( have_rows('prosess_trinn') ) : the_row();
$rad_indeks = get_row_index();
$er_partall = ( $rad_indeks % 2 === 0 );
$oppsett_klasse = $er_partall ? 'oppsett-speilvendt' : 'oppsett-standard';
$farge_klasse = $er_partall ? 'bg-lys-graa' : 'bg-hvit';
?>
<div class="trinn-kort <?php echo esc_attr( $oppsett_klasse . ' ' . $farge_klasse ); ?>">
<span class="trinn-nummer"><?php echo esc_html( sprintf( '%02d', $rad_indeks ) ); ?></span>
<div class="trinn-detaljer">
<h4><?php echo esc_html( get_sub_field('overskrift') ); ?></h4>
<p><?php echo esc_html( get_sub_field('kort_tekst') ); ?></p>
</div>
</div>
<?php endwhile; ?>
</div>
<?php endif; ?>Fremheve hvert tredje element med modulo
Modulo-operatøren (%) gir resten ved heltallsdivisjon. Dette gir presis kontroll over gjentakende mønstre:
- Annenhver rad (partall/oddetall):
$rad_indeks % 2 === 0 - Hvert tredje kort i et rutenett:
$rad_indeks % 3 === 0 - Første rad:
$rad_indeks === 1 - Siste rad: samkjøring med antall rader telt opp på forhånd.
Eksempel på å markere anbefalte produkter i et trespalters rutenett:
$modulo_tre = $rad_indeks % 3;
if ( $modulo_tre === 0 ) {
$kort_stil = 'kort-uthevet kort-tredje';
} elseif ( $modulo_tre === 1 ) {
$kort_stil = 'kort-standard kort-forste';
} else {
$kort_stil = 'kort-standard kort-andre';
}Hvordan style forste og siste rad i ACF Repeater
Når du ønsker å tilføre en spesiell toppstripe på første modul eller fjerne en bunnmarg på siste modul:
<?php
$alle_medlemmer = get_field('team_medlemmer');
$totalt_antall = is_array( $alle_medlemmer ) ? count( $alle_medlemmer ) : 0;
if ( have_rows('team_medlemmer') ) : ?>
<div class="team-rutenett">
<?php while ( have_rows('team_medlemmer') ) : the_row();
$posisjon = get_row_index();
$klasser = ['medlem-kort'];
if ( $posisjon === 1 ) {
$klasser[] = 'kort-forste';
}
if ( $posisjon === $totalt_antall ) {
$klasser[] = 'kort-siste';
}
if ( $posisjon % 2 === 0 ) {
$klasser[] = 'rad-partall';
} else {
$klasser[] = 'rad-oddetall';
}
?>
<div class="<?php echo esc_attr( implode( ' ', $klasser ) ); ?>">
<p class="navn"><?php echo esc_html( get_sub_field('fullt_navn') ); ?></p>
<p class="stilling"><?php echo esc_html( get_sub_field('stillingstittel') ); ?></p>
</div>
<?php endwhile; ?>
</div>
<?php endif; ?>Underfelt-logikk og layoutvariasjoner i maler
Styling trenger ikke bare å følge numeriske posisjoner. Ved å la redaktører velge kortvarianter fra en nedtrekksmeny i ACF, kan du styre layout og fargeaksenter dynamisk:
<?php if ( have_rows('prosjekt_faser') ) : ?>
<div class="prosjekt-tidslinje">
<?php while ( have_rows('prosjekt_faser') ) : the_row();
$indeks = get_row_index();
$kort_layout = get_sub_field('kort_type'); // 'fullbredde', 'to-kolonner', 'kompakt'
$aksent_farge = get_sub_field('aksent_farge');
$kort_klasser = [
'fase-modul',
'indeks-' . $indeks,
'type-' . sanitize_html_class( $kort_layout ?: 'standard' )
];
if ( $indeks % 2 === 0 ) {
$kort_klasser[] = 'retning-reversert';
}
?>
<section
class="<?php echo esc_attr( implode( ' ', $kort_klasser ) ); ?>"
style="<?php echo $aksent_farge ? esc_attr( '--aksent: ' . sanitize_hex_color( $aksent_farge ) . ';' ) : ''; ?>"
>
<?php
if ( $kort_layout === 'fullbredde' && $indeks === 1 ) {
get_template_part( 'template-parts/prosjekt/hero' );
} else {
get_template_part( 'template-parts/prosjekt/kort' );
}
?>
</section>
<?php endwhile; ?>
</div>
<?php endif; ?>Ved å bruke CSS-variabler (--aksent) og sanitizede klasser beholder du høy fleksibilitet uten å låse designet fast i stive PHP-betingelser.
Nar bor ACF-repeater-styling ligge i CSS i stedet for PHP
I henhold til prinsippet om ansvarsdeling bør presentasjonsregler håndteres i stilark når de ikke krever endringer i HTML-strukturen. Dersom endringen kun gjelder farger, marger eller flexbox-retning, gir CSS :nth-child() renere malfiler og bedre ytelse:
/* Sebrastriper for annenhver rad */
.prosess-trinn-beholder .trinn-kort:nth-child(even) {
background-color: #f8fafc;
}
/* Snu retningen for Flexbox i annenhver modul */
.prosess-trinn-beholder .trinn-kort {
display: flex;
flex-direction: row;
gap: 2rem;
align-items: center;
}
.prosess-trinn-beholder .trinn-kort:nth-child(even) {
flex-direction: row-reverse;
}
/* Fremheve hvert tredje kort i CSS-grid */
.tjenester-liste .tjeneste-kort:nth-child(3n) {
border: 2px solid #2563eb;
transform: scale(1.02);
}
/* Første og siste modul */
.prosess-trinn-beholder .trinn-kort:first-child {
border-top: 3px solid #0f172a;
}
.prosess-trinn-beholder .trinn-kort:last-child {
border-bottom: 3px solid #0f172a;
}Kriterier for a velge mellom CSS og PHP
- Bruk CSS
:nth-child(): Ved rene fargejusteringer, kantlinjer, bakgrunner og responsiv kolonnespeiling med media queries. - Bruk PHP modulo / indekser: Når HTML-strukturen varierer, ved inkludering av ulike malfiler med
get_template_part(), eller ved generering av strukturerte data for søkemotorer.
Integrasjon med ACF Flexible Content for fleksible seksjoner
Når gjentakende seksjoner krever fundamentalt forskjellige feltsett, blir et standard repeater-felt fort uoversiktlig for redaktøren. Flexible Content er da den foretrukne arkitekturen:
<?php if ( have_rows('side_moduler') ) : ?>
<main class="modul-strom">
<?php while ( have_rows('side_moduler') ) : the_row();
$layout_navn = get_row_layout();
$pos = get_row_index();
?>
<section class="seksjon-modul layout-<?php echo esc_attr( $layout_navn ); ?>" data-posisjon="<?php echo (int) $pos; ?>">
<?php
if ( $layout_navn === 'tekst_bilde_seksjon' ) :
get_template_part( 'template-parts/moduler/tekst-bilde', null, [ 'indeks' => $pos ] );
elseif ( $layout_navn === 'fordeler_rutenett' ) :
get_template_part( 'template-parts/moduler/fordeler', null, [ 'indeks' => $pos ] );
elseif ( $layout_navn === 'sitater_karusell' ) :
get_template_part( 'template-parts/moduler/sitater', null, [ 'indeks' => $pos ] );
endif;
?>
</section>
<?php endwhile; ?>
</main>
<?php endif; ?>Dette skiller ansvarsområdene, unngår unødige tomme rader i databasen og gir innholdsprodusenter et ryddig grensesnitt.
Gutenberg ACF-blokker med InnerBlocks og Block Context
I moderne blokktemaer erstatter egendefinerte Gutenberg-blokker tradisjonelle repeater-felter. Ved å registrere blokker via block.json og kombinere InnerBlocks med Block Context får redaktøren visuell kontroll direkte i editoren.
Registrering i block.json
Kind-blokkens block.json definerer foreldreforholdet og konteksten den lytter til:
{
"$schema": "https://schemas.wp.org/trunk/block.json",
"apiVersion": 3,
"name": "wppoland/prosess-kort",
"title": "Prosess Kort",
"category": "design",
"parent": [ "wppoland/prosess-beholder" ],
"usesContext": [ "wppoland/oppsettType" ],
"acf": {
"mode": "preview",
"renderTemplate": "render.php"
}
}Rendring av beholder og underblokker
I foreldreblokkens mal (prosess-beholder/render.php) definerer vi tillatte underblokker:
<div class="prosess-beholder-blokk">
<InnerBlocks
allowedBlocks="<?php echo esc_attr( wp_json_encode( ['wppoland/prosess-kort'] ) ); ?>"
template="<?php echo esc_attr( wp_json_encode( [ ['wppoland/prosess-kort'], ['wppoland/prosess-kort'] ] ) ); ?>"
/>
</div>I underblokkens mal (prosess-kort/render.php) leses konteksten ut direkte:
<?php
$oppsett = $context['wppoland/oppsettType'] ?? 'standard';
$tittel = get_field('kort_tittel');
$tekst = get_field('kort_innhold');
?>
<div class="prosess-kort-enhet stil-<?php echo esc_attr( $oppsett ); ?>">
<h4><?php echo esc_html( $tittel ); ?></h4>
<div><?php echo wp_kses_post( $tekst ); ?></div>
</div>Hvordan optimalisere ytelse i store ACF-repeatere
Når et repeater-felt inneholder mange rader eller er nøstet i flere nivåer, belastes wp_postmeta tungt. Hvis en overordnet repeater har 15 rader, og hver rad har 10 underelementer med 3 felt, lagres det over 500 metadata-rader per innlegg.
Hente hele repeateren med get_field
I stedet for while( have_rows() ), som oppdaterer pekere i minnet for hver iterasjon, kan man hente alle dataene samlet i en tabell:
$alle_trinn = get_field('prosess_trinn');
if ( ! empty( $alle_trinn ) && is_array( $alle_trinn ) ) {
echo '<div class="tabell-basert-liste">';
foreach ( $alle_trinn as $null_indeks => $rad ) {
$nummer = $null_indeks + 1;
$klasse = ( $nummer % 2 === 0 ) ? 'rad-partall' : 'rad-oddetall';
$tittel = $rad['overskrift'] ?? '';
$tekst = $rad['kort_tekst'] ?? '';
echo '<div class="' . esc_attr( $klasse ) . '">';
echo '<h4>' . esc_html( $tittel ) . '</h4>';
echo '<p>' . esc_html( $tekst ) . '</p>';
echo '</div>';
}
echo '</div>';
}Caching av ferdig HTML med Transients
For moduler som sjelden endres, gir mellomlagring av ferdig HTML via Transients API optimal ytelse:
function wppoland_get_cached_tjenester( int $post_id ): string {
$cache_nokkel = 'wppoland_tjenester_html_' . $post_id;
$html = get_transient( $cache_nokkel );
if ( false !== $html ) {
return $html;
}
if ( ! have_rows('tjeneste_elementer', $post_id) ) {
return '';
}
ob_start();
echo '<div class="tjenester-rutenett">';
while ( have_rows('tjeneste_elementer', $post_id) ) {
the_row();
$i = get_row_index();
$klasse = ( $i % 2 === 0 ) ? 'kort-partall' : 'kort-oddetall';
echo '<div class="' . esc_attr( $klasse ) . '">';
echo '<h3>' . esc_html( get_sub_field('tittel') ) . '</h3>';
echo '<div>' . wp_kses_post( get_sub_field('beskrivelse') ) . '</div>';
echo '</div>';
}
echo '</div>';
$html = ob_get_clean();
// Lagre i transient i 24 timer (86400 sekunder)
set_transient( $cache_nokkel, $html, 86400 );
return $html;
}
// Slette hurtigbuffer nar innlegget oppdateres
add_action('save_post', function( int $post_id ): void {
if ( ! wp_is_post_revision( $post_id ) && ! wp_is_post_autosave( $post_id ) ) {
delete_transient( 'wppoland_tjenester_html_' . $post_id );
}
});Nostede underfelt og ACF i WP REST API
Når nettsteder bygges med frakoblet WordPress (headless) mot moderne rammeverk som Astro eller Next.js, leveres repeater-data via REST API. Med register_rest_field kan responsen forenkles og klargjøres på serversiden:
add_action('rest_api_init', function(): void {
register_rest_field('page', 'tjenester_api', [
'get_callback' => function( array $post_arr ): array {
$post_id = $post_arr['id'];
$rader = get_field('tjeneste_elementer', $post_id);
if ( empty( $rader ) || ! is_array( $rader ) ) {
return [];
}
return array_map( function( array $rad, int $nokkel ): array {
return [
'posisjon' => $nokkel + 1,
'erPartall' => ( ( $nokkel + 1 ) % 2 === 0 ),
'tittel' => sanitize_text_field( $rad['tittel'] ?? '' ),
'tekst' => wp_kses_post( $rad['beskrivelse'] ?? '' ),
];
}, $rader, array_keys( $rader ) );
},
'schema' => [
'description' => 'Forenklede tjeneste-rader for headless frontend.',
'type' => 'array',
],
]);
});Modulær komponentoppdeling med get_template_part
For å bevare god kodekvalitet og unngå store malfiler, anbefales det å splitte repeater-elementer ut i egne delmaler:
<?php if ( have_rows('pris_pakker') ) : ?>
<div class="priser-rutenett">
<?php while ( have_rows('pris_pakker') ) : the_row();
$indeks = get_row_index();
$pakke_data = [
'indeks' => $indeks,
'er_partall' => ( $indeks % 2 === 0 ),
'navn' => get_sub_field('pakke_navn'),
'pris' => get_sub_field('pakke_pris'),
'fordeler' => get_sub_field('fordelsliste'),
];
get_template_part( 'template-parts/komponenter/priskort', null, $pakke_data );
?>
<?php endwhile; ?>
</div>
<?php endif; ?>Delmalen template-parts/komponenter/priskort.php håndterer deretter sin egen presentasjon uavhengig av overordnet struktur.
Feilsoking og vanlige fallgruver i repeater-lokker
- Uendelig løkke ved manglende the_row(): Hvis
the_row()utelates, flyttes ikke markøren fremover, ogget_row_index()vil returnere 1 i det uendelige inntil serverens minne eller tidsgrense overskrides. - Kall til get_field() inne i repeateren: Å kalle
get_field()i stedet forget_sub_field()resulterer i tomme verdier eller utilsiktet henting av globale innleggsdata. - TypeError i PHP 8 ved count(): Utførelse av
count()på et tomt eller usatt repeater-felt kaster feil i moderne PHP. Kontroller alltid medis_array()først. - CSS nth-child treffer feil element: Dersom eksterne JavaScript-biblioteker som glide- eller karusell-plugins pakker inn elementene i ekstra
<div>-tagger, må CSS-selektorene rettes mot det faktiske foreldreelementet.
Ofte stilte spørsmål om ACF Repeater
Starter get_row_index på 0 eller 1
Hva er forskjellen mellom get_row_index og get_row_layout
Hvor mange rader kan en ACF Repeater inneholde for nettstedet blir tregt
Praktisk implementering: pristabell med fremhevet pakke
For å eksemplifisere samspillet mellom PHP-logikk, underfelt og CSS-styling, ser vi på en komplett implementering av en pristabell. I dette scenariet legger kunden inn abonnementspakker i et repeater-felt, og malen håndterer automatisk:
- Markering av den mest populære pakken via et valg i editoren.
- Automatisk tildeling av klasser for partall og oddetall for å variere overflatefargene.
- Tilleggsklasser for første og siste kort i rutenettet.
<?php if ( have_rows('abonnement_pakker') ) :
$alle_pakker = get_field('abonnement_pakker');
$antall = is_array( $alle_pakker ) ? count( $alle_pakker ) : 0;
?>
<section class="priser-seksjon" id="priser" aria-label="<?php esc_attr_e('Pristabell', 'wppoland'); ?>">
<div class="priser-rutenett">
<?php while ( have_rows('abonnement_pakker') ) : the_row();
$indeks = get_row_index();
$pakke_navn = get_sub_field('pakke_tittel');
$pris_verdi = get_sub_field('pris_belop');
$er_anbefalt = (bool) get_sub_field('marker_som_anbefalt');
$egenskaper = get_sub_field('egenskaper_liste');
$kort_klasser = [ 'pris-kort', 'posisjon-' . $indeks ];
if ( $er_anbefalt ) {
$kort_klasser[] = 'kort-anbefalt';
}
if ( $indeks === 1 ) {
$kort_klasser[] = 'kort-grunnpakke';
}
if ( $indeks === $antall ) {
$kort_klasser[] = 'kort-bedrift';
}
$kort_klasser[] = ( $indeks % 2 === 0 ) ? 'nyanse-b' : 'nyanse-a';
?>
<div class="<?php echo esc_attr( implode( ' ', $kort_klasser ) ); ?>">
<?php if ( $er_anbefalt ) : ?>
<span class="merke-bestseller"><?php esc_html_e('Mest populær', 'wppoland'); ?></span>
<?php endif; ?>
<h3 class="kort-tittel"><?php echo esc_html( $pakke_navn ); ?></h3>
<div class="kort-pris"><?php echo esc_html( $pris_verdi ); ?></div>
<?php if ( ! empty( $egenskaper ) && is_array( $egenskaper ) ) : ?>
<ul class="kort-fordeler">
<?php foreach ( $egenskaper as $punkt ) : ?>
<li><?php echo esc_html( $punkt['egenskap_tekst'] ?? '' ); ?></li>
<?php endforeach; ?>
</ul>
<?php endif; ?>
<div class="kort-handling">
<a href="#kontakt" class="knapp-bestill"><?php esc_html_e('Velg abonnement', 'wppoland'); ?></a>
</div>
</div>
<?php endwhile; ?>
</div>
</section>
<?php endif; ?>Stilarket definerer rutenettet med CSS Grid, noe som sikrer en responsiv og ryddig visning på alle skjermformater:
.priser-rutenett {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
gap: 2rem;
align-items: stretch;
}
.pris-kort {
border-radius: 12px;
padding: 2rem;
background: #ffffff;
border: 1px solid #e2e8f0;
transition: transform 0.25s ease, box-shadow 0.25s ease;
}
.pris-kort:hover {
transform: translateY(-4px);
box-shadow: 0 12px 24px -8px rgba(15, 23, 42, 0.12);
}
.pris-kort.kort-anbefalt {
border: 2px solid #2563eb;
position: relative;
box-shadow: 0 20px 25px -5px rgba(37, 99, 235, 0.1);
}
.pris-kort.nyanse-b {
background-color: #f8fafc;
}Databaserelasjoner og flerspråklig synkronisering
I store flerspråklige WordPress-installasjoner som driftes med WPML eller Polylang, opprettes det separate innlegg for hver språkversjon. Dette medfører at hvert språk har sine egne oppføringer i wp_postmeta.
Dersom du oversetter en side og benytter en sentralisert mal som henter data på tvers av språk, må du alltid sende med det eksplisitte innleggs-ID-et:
$oversatt_id = apply_filters( 'wpml_object_id', get_the_ID(), 'page', true, 'nb' );
if ( have_rows( 'felles_repeater', $oversatt_id ) ) {
while ( have_rows( 'felles_repeater', $oversatt_id ) ) {
the_row();
// Hent underfelt med korrekt språk-kontekst
}
}Uten eksplisitt ID vil ACF hente verdier fra den aktive forespørselen, noe som kan gi uoverensstemmelser i indeksering og oversettelser.
Sikkerhet og sanitering av underfelt i produksjon
Sikkerhet er avgjørende når gjentakende data rendres i temaet. Selv om innholdet legges inn av påloggede redaktører, må all utdata vaskes for å forhindre sikkerhetshull:
- Tekst og titler: Bruk
esc_html()for å uskadeliggjøre uønsket HTML i tekststrenger. - HTML-attributter: Bruk
esc_attr()for klasser, data-attributter og identifikatorer. - Lenker og URL-er: Valider alle lenkefelter med
esc_url(). - Rik tekst og formaterte felt: Benytt
wp_kses_post()for felt som skal tillate standard HTML-tagger som avsnitt, lister og fet skrift. - Responsiv bildehåndtering: Sett alltid returverdien i ACF til Image ID og benytt
wp_get_attachment_image(), noe som automatisk genererer korrektesrcset- ogsizes-attributter.
Håndtering av dype nøstinger og relasjoner mellom repeater-felt
I mer avanserte prosjekter hender det ofte at et repeater-felt inneholder et annet repeater-felt (nøstet repeater), for eksempel en liste over avdelinger der hver avdeling har en intern liste over ansatte.
<?php if ( have_rows('firma_avdelinger') ) : ?>
<div class="avdelinger-oversikt">
<?php while ( have_rows('firma_avdelinger') ) : the_row();
$avdeling_indeks = get_row_index();
$avdeling_navn = get_sub_field('avdelingsnavn');
?>
<section class="avdeling-seksjon avdeling-<?php echo esc_attr( $avdeling_indeks ); ?>">
<h2 class="avdeling-tittel"><?php echo esc_html( $avdeling_navn ); ?></h2>
<?php if ( have_rows('avdeling_ansatte') ) : ?>
<ul class="ansatte-liste">
<?php while ( have_rows('avdeling_ansatte') ) : the_row();
$ansatt_indeks = get_row_index();
$navn = get_sub_field('navn');
$tittel = get_sub_field('stilling');
$rad_klasse = ( $ansatt_indeks % 2 === 0 ) ? 'rad-b' : 'rad-a';
?>
<li class="ansatt-kort <?php echo esc_attr( $rad_klasse ); ?>">
<strong><?php echo esc_html( $navn ); ?></strong>
<span><?php echo esc_html( $tittel ); ?></span>
</li>
<?php endwhile; ?>
</ul>
<?php endif; ?>
</section>
<?php endwhile; ?>
</div>
<?php endif; ?>Legg merke til at funksjonen get_row_index() inne i den indre løkken avdeling_ansatte gir den lokale posisjonen til den ansatte innenfor den aktuelle avdelingen, og ikke den overordnede avdelingsindeksen. Dette gjør det enkelt å lage lokale sebrastriper og lister.
Caching-strategier og minneoptimalisering med Redis
På store portaler med hundrevis av besøkende i minuttet kan selv et optimalisert repeater-oppsett medføre unødvendig ressursbruk dersom WordPress må instansiere feltobjekter for hver enkelt forespørsel.
Hvorfor oppstår minneproblemer ved store datasett
- Serialisering i Object Cache: WordPress laster inn alle innleggsmetadata i en intern hurtigbuffer via
update_postmeta_cache(). Dersom repeater-feltet inneholder mange bilder og lange tekster, kan det serialiserte objektet i Redis overstige flere hundre kilobyte. - PHP-minnebruk ved mange underfelt: Hver iterasjon av
the_row()krever minne for å holde referanser til feltdefinisjonene. På servere med begrensede minnerammer (memory_limit = 128M) kan store datamengder utløse minnefeil. - Database-fragmentering ved hyppig oppdatering: Hver gang en redaktør lagrer et innlegg med mange repeater-rader, utfører WordPress slettinger og nye innsettinger i
wp_postmeta, noe som over tid fragmenterer databasetabellen.
Ved å ta i bruk fragmentcaching med Transients eller bygge løsningen om til modulære Gutenberg-blokker, avlastes PHP-motoren og MySQL-databasen betydelig.
Feilsøking og feilretting av vanlige stylingproblemer
Når utviklere jobber med avanserte oppsett i ACF, oppstår det av og til situasjoner der klasser eller stiler ikke oppfører seg som forventet:
- Stiler forsvinner etter oppdatering av temaet: Kontroller om overskrivende CSS-regler i temaets stilark har høyere spesifisitet enn klassene som injiseres fra PHP.
- Karusell-skript bryter :nth-child: Biblioteker som Swiper eller Slick legger ofte til ekstra omrammende elementer (
swiper-wrapper,swiper-slide). Da vil direkte barnevelgere som.beholder > :nth-child(even)ikke treffe. Løsningen er å legge klassen direkte på elementet via PHP. - Paginering av store repeater-lister: Hvis du har over 50 rader og ikke ønsker å laste alle samtidig, kan du dele listen opp i mindre deler ved hjelp av
array_slice()basert på gjeldende sidenummer (paged). - Håndtering av tomme underfelt: Sjekk alltid om verdien eksisterer før du genererer HTML-omramminger, slik at du unngår tomme tagger som skaper uønskede marger i oppsettet.
Oppsummering og sjekkliste for utviklere
ACF Repeater gir god balanse mellom redaksjonell fleksibilitet og ryddig kodebase. Ved å kombinere get_row_index(), modulo-operatøren og moderne CSS-pseudoklasser :nth-child(), oppnås en modulær og robust arkitektur.
Husk følgende i produksjon:
- Sjekk alltid med
have_rows()og huskthe_row(). - Bruk utelukkende
get_sub_field()inne i repeater-løkken. - Rengjør all utdata med
esc_html(),esc_attr()ogwp_kses_post(). - Flytt ren layout og fargevariasjoner til CSS
:nth-child(). - Bruk Transients eller blokker med
InnerBlocksfor å beskyttewp_postmetamot overbelastning.
Trenger du assistanse med å bygge avanserte Gutenberg-blokker eller optimalisere store WordPress-løsninger? Les mer om våre tjenester for WordPress-utvikling eller få profesjonell hjelp med drift og vedlikehold av WordPress.







