Advanced custom fields (ACF) - Pola powtarzalne i stylowanie

Advanced custom fields (ACF) - Pola powtarzalne i stylowanie

Ostatnio zweryfikowano: 22 września 2026
8 min czytania
Case study
Full-stack developer
Wtyczka Advanced Custom Fields (ACF) w wersji Pro to standard w profesjonalnym tworzeniu dedykowanych motywów WordPress. Jej najważniejszą funkcją jest bez wątpienia pole powtarzalne (Repeater Field). Umożliwia ono edytorom tworzenie nieograniczonej liczby powtarzalnych elementów: harmonogramów konferencji, cenników, list członków zespołu, kroków procesu zakupowego czy rozbudowanych galerii.

Często jednak pojawia się wyzwanie projektowe: jak w ramach jednej pętli kontrolować wygląd poszczególnych elementów? Designerzy nierzadko projektują układ naprzemienny (tzw. układ zygzakowaty, gdzie wiersze nieparzyste mają tekst po lewej i zdjęcie po prawej, a wiersze parzyste na odwrót), zróżnicowane kolory tła czy wyróżnienie co trzeciej karty ofertowej.

W tym artykule inżynieryjnym dowiesz się, jak prawidłowo obsługiwać pętlę have_rows(), jak wykorzystać operator modulo oraz wbudowane funkcje indeksujące ACF, kiedy przenieść ciężar stylowania do arkuszy CSS za pomocą :nth-child(), a przede wszystkim: jak pole powtarzalne wpływa na bazę danych MySQL i jak uniknąć drastycznego spadku wydajności przy setkach wierszy.

#Jak działa pole Repeater wewnątrz bazy danych WordPress

Zanim napiszemy pierwszą linijkę kodu w szablonie PHP, musimy zrozumieć architekturę danych ACF. W przeciwieństwie do relacyjnych baz projektowanych od zera, WordPress przechowuje metadane w płaskiej strukturze klucz-wartość w tabeli wp_postmeta.

Gdy dodajesz pole typu Repeater o nazwie sekcje_oferty z dwoma podpólami (tytul oraz opis) i wypełniasz 5 wierszy, ACF tworzy w bazie:

  1. Rekord główny: sekcje_oferty z wartością 5 (liczba wierszy).
  2. Rekordy dla wiersza 0: sekcje_oferty_0_tytul oraz sekcje_oferty_0_opis.
  3. Rekordy dla wiersza 1: sekcje_oferty_1_tytul oraz sekcje_oferty_1_opis.
  4. …oraz ukryte rekordy systemowe definiujące klucze pól (_sekcje_oferty_0_tytul = field_65a1...).

Dla 10 wierszy i 4 podpól oznacza to wygenerowanie ponad 80 osobnych wpisów w tabeli wp_postmeta. Świadomość tej struktury jest fundamentalna: niewłaściwe odpytywanie repeatera w pętli szablonu może doprowadzić do lawiny zapytań SQL i opóźnień w generowaniu strony (TTFB).

#Podstawowa pętla have_rows() i the_row()

Oficjalny sposób iteracji po repeaterze opiera się na zestawie funkcji have_rows() i the_row():

<?php if ( have_rows('sekcje_oferty') ) : ?>
    <section class="oferta-repeater-wrapper">
        <?php while ( have_rows('sekcje_oferty') ) : the_row(); 
            // Pobieranie danych bieżącego wiersza
            $tytul = get_sub_field('tytul');
            $tekst = get_sub_field('tekst');
            $grafika = get_sub_field('grafika');
        ?>
            <article class="oferta-karta">
                <?php if ( ! empty( $grafika ) ) : ?>
                    <div class="oferta-grafika">
                        <?php echo wp_get_attachment_image( $grafika['id'], 'medium_large' ); ?>
                    </div>
                <?php endif; ?>
                <div class="oferta-tresc">
                    <h3><?php echo esc_html( $tytul ); ?></h3>
                    <div class="opis"><?php echo wp_kses_post( $tekst ); ?></div>
                </div>
            </article>
        <?php endwhile; ?>
    </section>
<?php endif; ?>

Funkcja the_row() przesuwa wewnętrzny wskaźnik tablicy wierszy i ustawia kontekst dla get_sub_field(). Nigdy nie używaj wewnątrz repeatera standardowej funkcji get_field('tytul') zamiast get_sub_field('tytul'), ponieważ spowoduje to błąd lub pobranie niepowiązanej wartości z poziomu wpisu.

#Zróżnicowane stylowanie w PHP: Indeksy i operator modulo

Gdy projekt wymaga, aby co drugi element miał inny kolor tła, odwrócony układ kolumn lub inną klasę CSS, do gry wkracza logika warunkowa.

#Podejście 1: Wbudowana funkcja get_row_index()

Wielu programistów tworzy manualną zmienną pomocniczą $i = 0 przed pętlą. Choć działa to poprawnie, ACF oferuje wbudowaną funkcję pomocniczą get_row_index(). Zwraca ona numer bieżącego wiersza, zaczynając od 1 (wiersz pierwszy = 1, drugi = 2 itd.):

<?php if ( have_rows('bloki_procesu') ) : ?>
    <div class="proces-kontener">
        <?php while ( have_rows('bloki_procesu') ) : the_row(); 
            $indeks = get_row_index();
            // Sprawdzamy, czy wiersz jest parzysty
            $czy_parzysty = ( $indeks % 2 === 0 );
            $klasa_ukladu = $czy_parzysty ? 'uklad-odwrocony' : 'uklad-standardowy';
            $klasa_tla    = $czy_parzysty ? 'bg-szary' : 'bg-bialy';
        ?>
            <div class="krok-procesu <?php echo esc_attr( $klasa_ukladu . ' ' . $klasa_tla ); ?>">
                <span class="numer-kroku">0<?php echo (int) $indeks; ?></span>
                <div class="tresc-kroku">
                    <h3><?php echo esc_html( get_sub_field('naglowek') ); ?></h3>
                    <p><?php echo esc_html( get_sub_field('krotki_opis') ); ?></p>
                </div>
            </div>
        <?php endwhile; ?>
    </div>
<?php endif; ?>

#Zaawansowane sekwencje: Wyróżnienie co trzeciego elementu

Operator modulo (%) oblicza resztę z dzielenia liczb całkowitych. Daje to ogromne możliwości sterowania układem:

  • Układ naprzemienny (A/B): $indeks % 2 === 0
  • Wyróżnienie co 3. kafelka w siatce (np. karta polecana): $indeks % 3 === 0
  • Specjalny styl dla pierwszego elementu: $indeks === 1 lub funkcja get_row_layout() przy elastycznych polach.

Oto przykład oznaczania kart polecanych w siatce trójkolumnowej:

$is_featured = ( $indeks % 3 === 0 );
$badge = $is_featured ? '<span class="badge-bestseller">Polecane</span>' : '';

#Alternatywa w arkuszu stylów: Nowoczesne selektory CSS

Zasada czystego kodu mówi: nie używaj logiki backendowej do zadań, które można zrealizować w czystym arkuszu stylów. Jeśli zmiana w wierszu dotyczy wyłącznie koloru tła, marginesów czy odwrócenia kolejności za pomocą Flexboksa, kod PHP powinien pozostać maksymalnie prosty, a stylowanie należy przekazać do CSS.

Dzięki nowoczesnym pseudoklasom CSS :nth-child() możemy uzyskać perfekcyjny układ bez zanieczyszczania szablonu PHP klasami technicznymi:

/* Zmiana tła dla parzystych wierszy (efekt zebry) */
.proces-kontener .krok-procesu:nth-child(even) {
  background-color: #f8fafc;
}

/* Odwrócenie kierunku układu Flexbox w co drugim bloku */
.proces-kontener .krok-procesu:nth-child(even) {
  flex-direction: row-reverse;
}

/* Wyróżnienie co trzeciej karty w siatce CSS Grid */
.oferta-repeater-wrapper .oferta-karta:nth-child(3n) {
  border: 2px solid #2563eb;
  transform: scale(1.02);
}

/* Ukrycie separatora na ostatnim elemencie */
.proces-kontener .krok-procesu:last-child .linia-lacząca {
  display: none;
}

#Kiedy PHP jest bezwzględnie konieczne?

CSS :nth-child nie wystarczy w następujących scenariuszach:

  1. Różna struktura znaczników HTML: Gdy wiersze nieparzyste wymagają dodatkowego kontenera <div> lub tagu <aside>, którego nie ma w wierszach parzystych.
  2. Ładowanie różnych plików szablonów: Gdy w zależności od iteracji wywołujesz inne funkcje get_template_part('template-parts/karta', 'promowana').
  3. Generowanie danych ustrukturyzowanych Schema.org: Gdy parametr position w JSON-LD musi być wyliczony liczbowo w PHP.

#Wydajność: Jak przyspieszyć duże pole Repeater

Gdy edytor wprowadzi do repeatera 50 lub 100 wierszy, strona może zwolnić nawet o kilkaset milisekund. Wynika to z faktu, że każda funkcja get_sub_field() wewnątrz pętli odpytuje pamięć podręczną WordPressa o poszczególne klucze.

#1. Pobranie wszystkich danych na raz za pomocą get_field()

Zamiast pętli while(have_rows()), która inkrementuje stan wewnętrzny w każdym obrocie, w przypadku bardzo dużych repeaterów szybsze bywa pobranie całej tablicy wielowymiarowej jednym wywołaniem:

$wszystkie_sekcje = get_field('sekcje_oferty');

if ( ! empty( $wszystkie_sekcje ) && is_array( $wszystkie_sekcje ) ) {
    foreach ( $wszystkie_sekcje as $index => $wiersz ) {
        // Dane są już w tablicy PHP, brak wywołań get_sub_field()
        $tytul = $wiersz['tytul'] ?? '';
        $opis  = $wiersz['tekst'] ?? '';
        // Indeksowanie od zera lub zera + 1
        $numer = $index + 1;
    }
}

#2. Buforowanie wyrenderowanego HTML (Transients API)

Dla powtarzalnych bloków, które zmieniają się rzadko (np. cenniki czy sekcje FAQ oparte na repeaterze), najlepszą praktyką inżynieryjną jest buforowanie gotowego kodu HTML w tabeli transientów lub pamięci Redis:

function wppoland_render_sekcje_oferty_cached( int $post_id ): void {
    $cache_key = 'wppoland_repeater_oferta_' . $post_id;
    $html = get_transient( $cache_key );

    if ( false === $html ) {
        ob_start();
        if ( have_rows('sekcje_oferty', $post_id) ) {
            echo '<div class="oferta-grid">';
            while ( have_rows('sekcje_oferty', $post_id) ) {
                the_row();
                // Renderowanie wiersza...
            }
            echo '</div>';
        }
        $html = ob_get_clean();
        
        // Zapis do pamięci podręcznej na 24 godziny (86400 s)
        set_transient( $cache_key, $html, 86400 );
    }

    echo $html;
}

// Czyszczenie pamięci podręcznej przy zapisie wpisu
add_action('save_post', function (int $post_id): void {
    delete_transient('wppoland_repeater_oferta_' . $post_id);
});

Dzięki takiemu zabiegowi złożony repeater z 50 wierszami renderuje się w ułamku milisekundy dla każdego kolejnego odwiedzającego.

#Często zadawane pytania (FAQ)

#Czym różni się get_row_index() od ręcznego licznika $i++?

Funkcja get_row_index() jest natywną metodą ACF, która automatycznie zwraca pozycję bieżącego wiersza w pętli, zaczynając od liczby 1. Zwalnia programistę z konieczności deklarowania i inkrementowania zmiennych pomocniczych przed i wewnątrz pętli.

#Czy lepiej używać klas PHP czy selektorów CSS :nth-child?

Do czysto wizualnych modyfikacji (kolor tła, zmiana kierunku flexbox, marginesy) zdecydowanie zaleca się CSS :nth-child(). Zmniejsza to objętość kodu HTML i separuje warstwę prezentacji. PHP jest konieczne, gdy parzystość determinuje odmienną strukturę tagów HTML lub ładowanie różnych szablonów.

#Jak bezpiecznie pobierać obrazy wewnątrz repeatera?

W konfiguracji pola ACF ustaw zwracany format jako Image ID lub Image Array. W kodzie PHP najlepiej wykorzystać identyfikator i wywołać wp_get_attachment_image($grafika['id'], 'rozmiar'), co automatycznie wygeneruje atrybuty srcset, sizes oraz nowoczesne leniwe ładowanie.

#Dlaczego strona z dużym polem Repeater ładuje się powoli?

ACF przechowuje każdą komórkę repeatera jako osobny wpis w tabeli wp_postmeta. Przy dziesiątkach wierszy baza danych musi przetworzyć setki zapytań o pojedyncze metadane. Rozwiązaniem jest buforowanie fragmentów HTML (Transients API) lub optymalizacja zapytań poprzez jednorazowe pobranie tablicy get_field().

#Podsumowanie

Pole powtarzalne ACF daje edytorom ogromną elastyczność w zarządzaniu treścią, a deweloperom pozwala budować zaawansowane, modułowe szablony. Umiejętne łączenie funkcji indeksujących get_row_index(), logiki warunkowej w PHP oraz nowoczesnych reguł CSS :nth-child() gwarantuje czysty, czytelny kod i pełną kontrolę nad wyglądem.

Potrzebujesz pomocy w optymalizacji skomplikowanego motywu WordPress opartego o ACF lub chcesz zlecić stworzenie dedykowanych bloków Gutenberg? Sprawdź ofertę naszych usług programistycznych jako doświadczony WordPress developer lub skonsultuj się z nami w ramach utrzymania stron WordPress.

Następny krok

Przekuj artykuł w realne wdrożenie

Pod tym wpisem dokładam linki, które domykają intencję użytkownika i prowadzą dalej w strukturze serwisu.

Chcesz wdrożyć ten temat na swojej stronie?

Jeśli chcesz przełożyć wiedzę z artykułu na działającą stronę, sklep albo przebudowę serwisu, przygotuję konkretny zakres prac.

Powiązany klaster

Sprawdź inne usługi WordPress i bazę wiedzy

Wzmocnij swój biznes dzięki profesjonalnemu wsparciu technicznemu w kluczowych obszarach ekosystemu WordPress.

Polecane artykuły