ACF repeater fields - Hvordan style spesifikke rader

ACF repeater fields - Hvordan style spesifikke rader

Sist verifisert: 13. oktober 2026
18 min lesetid
Casestudie
Full-stack-utvikler

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:

  1. En tellerrad: meta_key = 'tjeneste_elementer' med meta_value = '4'.
  2. Metadata for rad 0: tjeneste_elementer_0_tittel og tjeneste_elementer_0_beskrivelse.
  3. Metadata for rad 1: tjeneste_elementer_1_tittel og tjeneste_elementer_1_beskrivelse.
  4. …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

  1. Uendelig løkke ved manglende the_row(): Hvis the_row() utelates, flyttes ikke markøren fremover, og get_row_index() vil returnere 1 i det uendelige inntil serverens minne eller tidsgrense overskrides.
  2. Kall til get_field() inne i repeateren: Å kalle get_field() i stedet for get_sub_field() resulterer i tomme verdier eller utilsiktet henting av globale innleggsdata.
  3. TypeError i PHP 8 ved count(): Utførelse av count() på et tomt eller usatt repeater-felt kaster feil i moderne PHP. Kontroller alltid med is_array() først.
  4. 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

I ACF returnerer get_row_index() alltid verdier som starter på 1 for den første raden. For modulo-beregning av partall og oddetall sjekker du ($indeks % 2 === 0) for partall og ($indeks % 2 !== 0) for oddetall.

#Hva er forskjellen mellom get_row_index og get_row_layout

Funksjonen get_row_index() returnerer radens tallposisjon i løkken, mens get_row_layout() brukes i Flexible Content-felter for å returnere navnet på den aktive layout-typen.

#Hvor mange rader kan en ACF Repeater inneholde for nettstedet blir tregt

Siden hvert underfelt lagres som en egen rad i wp_postmeta, kan repeater-felter med over 30 til 50 rader forsinke innlastingen betydelig. For store datamengder bør du bruke transients-caching, hente data samlet med get_field(), eller vurdere tilpassede innleggstyper.

#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:

  1. Tekst og titler: Bruk esc_html() for å uskadeliggjøre uønsket HTML i tekststrenger.
  2. HTML-attributter: Bruk esc_attr() for klasser, data-attributter og identifikatorer.
  3. Lenker og URL-er: Valider alle lenkefelter med esc_url().
  4. Rik tekst og formaterte felt: Benytt wp_kses_post() for felt som skal tillate standard HTML-tagger som avsnitt, lister og fet skrift.
  5. Responsiv bildehåndtering: Sett alltid returverdien i ACF til Image ID og benytt wp_get_attachment_image(), noe som automatisk genererer korrekte srcset- og sizes-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

  1. 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.
  2. 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.
  3. 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:

  1. Stiler forsvinner etter oppdatering av temaet: Kontroller om overskrivende CSS-regler i temaets stilark har høyere spesifisitet enn klassene som injiseres fra PHP.
  2. 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.
  3. 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).
  4. 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:

  1. Sjekk alltid med have_rows() og husk the_row().
  2. Bruk utelukkende get_sub_field() inne i repeater-løkken.
  3. Rengjør all utdata med esc_html(), esc_attr() og wp_kses_post().
  4. Flytt ren layout og fargevariasjoner til CSS :nth-child().
  5. Bruk Transients eller blokker med InnerBlocks for å beskytte wp_postmeta mot 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.

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.

Relaterte artikler