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:
- Rekord główny:
sekcje_ofertyz wartością5(liczba wierszy). - Rekordy dla wiersza 0:
sekcje_oferty_0_tytulorazsekcje_oferty_0_opis. - Rekordy dla wiersza 1:
sekcje_oferty_1_tytulorazsekcje_oferty_1_opis. - …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 === 1lub funkcjaget_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:
- Różna struktura znaczników HTML: Gdy wiersze nieparzyste wymagają dodatkowego kontenera
<div>lub tagu<aside>, którego nie ma w wierszach parzystych. - Ł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'). - Generowanie danych ustrukturyzowanych Schema.org: Gdy parametr
positionw 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++?
Czy lepiej używać klas PHP czy selektorów CSS :nth-child?
Jak bezpiecznie pobierać obrazy wewnątrz repeatera?
Dlaczego strona z dużym polem Repeater ładuje się powoli?
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.







