No entanto, à medida que os requisitos de design evoluem, surge invariavelmente a necessidade de aplicar estilos diferenciados: layouts em ziguezague com imagens alternadas à esquerda e à direita, fundos com tonalidades contrastantes tipo “zebra”, ou destaque visual especial para o primeiro ou último elemento da lista.
Neste artigo técnico, exploramos a fundo o funcionamento dos ciclos de iteração do ACF, a anatomia de armazenamento na base de dados MySQL, padrões de código PHP limpos e a divisão equilibrada entre lógica de servidor e seletores CSS modernos.
Como o repeater ACF e guardado na base de dados
Para escrever código limpo e performante, os programadores devem compreender como o WordPress e o ACF armazenam valores nos bastidores. O WordPress regista os metadados dos artigos na tabela wp_postmeta sob uma estrutura chave-valor simples.
Quando cria um campo Repeater denominado blocos_servicos com dois subcampos (titulo e descricao), e o editor preenche 4 linhas, o ACF gera os seguintes registos na base de dados:
- Um registo de contagem:
meta_key = 'blocos_servicos'commeta_value = '4'. - Metadados da iteração 0:
blocos_servicos_0_tituloeblocos_servicos_0_descricao. - Metadados da iteração 1:
blocos_servicos_1_tituloeblocos_servicos_1_descricao. - …além de chaves de referência do sistema (
_blocos_servicos_0_titulo=field_65a12b8).
Para 10 linhas com 4 subcampos, isto representa mais de 80 registos individuais na tabela wp_postmeta para um único artigo. Esta estrutura demonstra claramente porque iterações descuidadas no frontend podem desencadear sobrecarga de consultas SQL e prejudicar o tempo de resposta do servidor (TTFB).
Como usar have_rows e the_row no ACF repeater
O padrão canónico para percorrer campos repetíveis baseia-se na combinação das funções have_rows() e the_row():
<?php if ( have_rows('blocos_servicos') ) : ?>
<section class="servicos-repeater-wrapper" aria-label="<?php esc_attr_e('Os Nossos Serviços', 'wppoland'); ?>">
<div class="servicos-grelha">
<?php while ( have_rows('blocos_servicos') ) : the_row();
$titulo = get_sub_field('titulo');
$descricao = get_sub_field('descricao');
$imagem = get_sub_field('imagem_destaque');
?>
<article class="servico-cartao">
<?php if ( ! empty( $imagem ) && is_array( $imagem ) ) : ?>
<div class="servico-media">
<?php echo wp_get_attachment_image( (int) $imagem['id'], 'medium_large' ); ?>
</div>
<?php endif; ?>
<div class="servico-conteudo">
<h3><?php echo esc_html( $titulo ); ?></h3>
<div class="texto"><?php echo wp_kses_post( $descricao ); ?></div>
</div>
</article>
<?php endwhile; ?>
</div>
</section>
<?php endif; ?>A função the_row() avança o apontador interno e define o contexto da linha atual. Dentro deste ciclo, é fundamental chamar get_sub_field() em vez de get_field(), pois esta última tentará aceder ao contexto global do artigo em vez da linha ativa.
Estilizacao condicional com PHP: get_row_index e operador modulo
Quando o projeto exige que linhas alternadas tenham cores de fundo distintas, disposições de colunas invertidas ou classes semânticas específicas, recorremos à lógica no servidor.
Como funciona get_row_index no ACF
Em vez de inicializar um contador manual ($i = 0) antes do ciclo, o ACF disponibiliza a função nativa get_row_index(). Ela retorna a posição ordinal da linha atual como um índice baseado em 1 (a primeira linha retorna 1, a segunda retorna 2, e assim sucessivamente):
<?php if ( have_rows('etapas_processo') ) : ?>
<div class="processo-lista-blocos">
<?php while ( have_rows('etapas_processo') ) : the_row();
$indice = get_row_index();
$e_par = ( $indice % 2 === 0 );
$classe_layout = $e_par ? 'disposicao-invertida' : 'disposicao-padrao';
$classe_fundo = $e_par ? 'bg-cinza-claro' : 'bg-branco';
?>
<div class="etapa-item <?php echo esc_attr( $classe_layout . ' ' . $classe_fundo ); ?>">
<span class="etapa-numero"><?php echo esc_html( sprintf( '%02d', $indice ) ); ?></span>
<div class="etapa-detalhes">
<h4><?php echo esc_html( get_sub_field('titulo_etapa') ); ?></h4>
<p><?php echo esc_html( get_sub_field('resumo_etapa') ); ?></p>
</div>
</div>
<?php endwhile; ?>
</div>
<?php endif; ?>Destacar cada terceiro elemento com o operador modulo
O operador módulo (%) calcula o resto da divisão inteira, permitindo criar padrões visuais repetitivos:
- Padrão alternado (linhas pares/ímpares):
$indice % 2 === 0 - Destacar cada terceiro elemento numa grelha:
$indice % 3 === 0 - Identificação do elemento inicial e final: comparação com
1e com a contagem total de registos.
Exemplo de atribuição de destaque a cada terceiro elemento:
$modulo_tres = $indice % 3;
if ( $modulo_tres === 0 ) {
$classe_cartao = 'cartao-destacado cartao-terciario';
} elseif ( $modulo_tres === 1 ) {
$classe_cartao = 'cartao-regular cartao-primario';
} else {
$classe_cartao = 'cartao-regular cartao-secundario';
}Como destacar o primeiro e o ultimo item do repeater
Quando é necessário aplicar uma borda especial no cabeçalho ou remover espaçamentos na última linha da listagem:
<?php
$dados_brutos = get_field('equipa_membros');
$total_linhas = is_array( $dados_brutos ) ? count( $dados_brutos ) : 0;
if ( have_rows('equipa_membros') ) : ?>
<div class="equipa-grelha-itens">
<?php while ( have_rows('equipa_membros') ) : the_row();
$posicao = get_row_index();
$classes = ['membro-card'];
if ( $posicao === 1 ) {
$classes[] = 'card-primeiro-item';
}
if ( $posicao === $total_linhas ) {
$classes[] = 'card-ultimo-item';
}
if ( $posicao % 2 === 0 ) {
$classes[] = 'linha-par';
} else {
$classes[] = 'linha-impar';
}
?>
<div class="<?php echo esc_attr( implode( ' ', $classes ) ); ?>">
<p class="nome"><?php echo esc_html( get_sub_field('nome_completo') ); ?></p>
<p class="cargo"><?php echo esc_html( get_sub_field('cargo_profissional') ); ?></p>
</div>
<?php endwhile; ?>
</div>
<?php endif; ?>Logica condicional baseada em subcampos e variacoes de layout
A estilização visual pode combinar a ordem numérica com opções selecionadas pelos utilizadores em subcampos do ACF:
<?php if ( have_rows('estudos_caso_seccoes') ) : ?>
<div class="estudos-fluxo-conteudo">
<?php while ( have_rows('estudos_caso_seccoes') ) : the_row();
$indice_linha = get_row_index();
$estilo_layout = get_sub_field('variante_exibicao'); // 'hero', 'dividido', 'compacto'
$cor_personalizada = get_sub_field('tom_acento');
$classes_contentor = [
'bloco-estudo',
'ordem-' . $indice_linha,
'variante-' . sanitize_html_class( $estilo_layout ?: 'padrao' )
];
if ( $indice_linha % 2 === 0 ) {
$classes_contentor[] = 'direcao-invertida';
}
?>
<section
class="<?php echo esc_attr( implode( ' ', $classes_contentor ) ); ?>"
style="<?php echo $cor_personalizada ? esc_attr( '--cor-acento: ' . sanitize_hex_color( $cor_personalizada ) . ';' ) : ''; ?>"
>
<?php
if ( $estilo_layout === 'hero' && $indice_linha === 1 ) {
get_template_part( 'template-parts/estudos/hero' );
} else {
get_template_part( 'template-parts/estudos/cartao' );
}
?>
</section>
<?php endwhile; ?>
</div>
<?php endif; ?>A atribuição de variáveis CSS customizadas (--cor-acento) mantém o código limpo e desacoplado de regras CSS rígidas.
Quando usar CSS em vez de PHP no repeater ACF
De acordo com as boas práticas de engenharia de software, qualquer alteração puramente cosmética que não modifique a semântica do documento HTML deve ser transferida para o CSS. Seletores modernos como :nth-child() oferecem maior performance e mantêm os templates PHP sucintos:
/* Efeito zebrado para linhas pares */
.processo-lista-blocos .etapa-item:nth-child(even) {
background-color: #f8fafc;
}
/* Inversão da ordem de colunas em Flexbox */
.processo-lista-blocos .etapa-item {
display: flex;
flex-direction: row;
gap: 2rem;
align-items: center;
}
.processo-lista-blocos .etapa-item:nth-child(even) {
flex-direction: row-reverse;
}
/* Destacar o terceiro elemento na grelha */
.servicos-grelha .servico-cartao:nth-child(3n) {
border: 2px solid #2563eb;
transform: scale(1.02);
}
/* Regras para primeiro e último elemento */
.processo-lista-blocos .etapa-item:first-child {
border-top: 3px solid #0f172a;
}
.processo-lista-blocos .etapa-item:last-child {
border-bottom: 3px solid #0f172a;
}Criterio para escolher entre CSS e PHP
- Usar CSS
:nth-child(): Para alternância de cores de fundo, sombras, bordas, e ajuste responsivo de colunas através de media queries. - Usar PHP (módulo e índices): Quando a estrutura de tags HTML muda (como
<article>versus<aside>), quando são chamados ficheiros de template distintos através deget_template_part(), ou na criação de esquemas JSON-LD.
Integracao com ACF Flexible Content para arquiteturas modulares
Quando as secções repetidas necessitam de campos totalmente divergentes, agrupar dezenas de opções num único campo Repeater torna o painel de administração confuso. O campo Flexible Content proporciona uma arquitetura superior:
<?php if ( have_rows('blocos_pagina') ) : ?>
<main class="pagina-blocos-stream">
<?php while ( have_rows('blocos_pagina') ) : the_row();
$nome_layout = get_row_layout();
$posicao = get_row_index();
?>
<section class="modulo-item modulo-<?php echo esc_attr( $nome_layout ); ?>" data-posicao="<?php echo (int) $posicao; ?>">
<?php
if ( $nome_layout === 'bloco_destaque_hero' ) :
get_template_part( 'template-parts/blocos/hero', null, [ 'indice' => $posicao ] );
elseif ( $nome_layout === 'grelha_vantagens' ) :
get_template_part( 'template-parts/blocos/vantagens', null, [ 'indice' => $posicao ] );
elseif ( $nome_layout === 'depoimentos_clientes' ) :
get_template_part( 'template-parts/blocos/depoimentos', null, [ 'indice' => $posicao ] );
endif;
?>
</section>
<?php endwhile; ?>
</main>
<?php endif; ?>O método get_row_layout() garante modularidade e previne a acumulação de valores nulos na base de dados.
Blocos Gutenberg nativos com ACF Blocks, InnerBlocks e Block Context
Nos temas modernos baseados em blocos (FSE), os campos repetíveis tradicionais dão lugar a blocos customizados de Gutenberg registados via block.json. A utilização de InnerBlocks e Block Context permite criar grelhas de cartões aninhadas e totalmente interativas.
Configuracao do bloco filho em block.json
O ficheiro block.json do bloco dependente estabelece o contexto partilhado:
{
"$schema": "https://schemas.wp.org/trunk/block.json",
"apiVersion": 3,
"name": "wppoland/cartao-etapa",
"title": "Cartão de Etapa",
"category": "design",
"parent": [ "wppoland/contentor-etapas" ],
"usesContext": [ "wppoland/tipoDisposicao" ],
"acf": {
"mode": "preview",
"renderTemplate": "render.php"
}
}Renderizacao com InnerBlocks e passagem de contexto
No ficheiro de renderização do contentor pai (contentor-etapas/render.php), inserimos a área de blocos filhos:
<div class="contentor-etapas-bloco">
<InnerBlocks
allowedBlocks="<?php echo esc_attr( wp_json_encode( ['wppoland/cartao-etapa'] ) ); ?>"
template="<?php echo esc_attr( wp_json_encode( [ ['wppoland/cartao-etapa'], ['wppoland/cartao-etapa'] ] ) ); ?>"
/>
</div>No template do bloco filho (cartao-etapa/render.php), lemos o contexto diretamente da variável $context:
<?php
$disposicao = $context['wppoland/tipoDisposicao'] ?? 'grelha';
$titulo = get_field('titulo_cartao');
$detalhes = get_field('detalhes_cartao');
?>
<div class="cartao-etapa-node formato-<?php echo esc_attr( $disposicao ); ?>">
<h4><?php echo esc_html( $titulo ); ?></h4>
<div><?php echo wp_kses_post( $detalhes ); ?></div>
</div>Esta abordagem confere aos editores a flexibilidade de arrastar e reordenar cartões visualmente no editor de blocos.
Como otimizar performance de repeaters ACF grandes na base de dados
Campos repetíveis com dezenas de linhas ou aninhamentos múltiplos multiplicam os registos na tabela wp_postmeta. Um repeater externo de 15 linhas contendo um repeater interno de 10 linhas e 3 subcampos gera mais de 500 registos por artigo.
1. Leitura direta em lote com get_field
Em situações de elevado volume de dados, carregar a estrutura completa para a memória PHP com uma única chamada a get_field() é significativamente mais rápido do que executar centenas de invocações a the_row() e get_sub_field():
$todos_itens = get_field('etapas_processo');
if ( ! empty( $todos_itens ) && is_array( $todos_itens ) ) {
echo '<div class="lista-array-direto">';
foreach ( $todos_itens as $zero_index => $linha ) {
$num = $zero_index + 1;
$classe = ( $num % 2 === 0 ) ? 'item-par' : 'item-impar';
$titulo = $linha['titulo_etapa'] ?? '';
$resumo = $linha['resumo_etapa'] ?? '';
echo '<div class="' . esc_attr( $classe ) . '">';
echo '<h4>' . esc_html( $titulo ) . '</h4>';
echo '<p>' . esc_html( $resumo ) . '</p>';
echo '</div>';
}
echo '</div>';
}2. Cache com WordPress Transients
Para secções que raramente são modificadas (como tabelas de preços ou secções de dúvidas frequentes), a melhor prática é guardar o HTML renderizado em cache:
function wppoland_obter_servicos_em_cache( int $post_id ): string {
$chave_cache = 'wppoland_servicos_html_' . $post_id;
$html_gerado = get_transient( $chave_cache );
if ( false !== $html_gerado ) {
return $html_gerado;
}
if ( ! have_rows('blocos_servicos', $post_id) ) {
return '';
}
ob_start();
echo '<div class="servicos-grelha-cache">';
while ( have_rows('blocos_servicos', $post_id) ) {
the_row();
$idx = get_row_index();
$classe = ( $idx % 2 === 0 ) ? 'cartao-par' : 'cartao-impar';
echo '<div class="' . esc_attr( $classe ) . '">';
echo '<h3>' . esc_html( get_sub_field('titulo') ) . '</h3>';
echo '<div>' . wp_kses_post( get_sub_field('descricao') ) . '</div>';
echo '</div>';
}
echo '</div>';
$html_gerado = ob_get_clean();
// Guardar durante 24 horas (86400 segundos)
set_transient( $chave_cache, $html_gerado, 86400 );
return $html_gerado;
}
// Limpeza de cache ao gravar o artigo
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_servicos_html_' . $post_id );
}
});Otimizacao de dados do repeater na WP REST API e Headless WordPress
Ao desenhar aplicações em arquitetura Headless com Next.js, Nuxt ou Astro, o tráfego da API REST deve ser enxuto. O uso de register_rest_field formata e higieniza os dados diretamente no backend:
add_action('rest_api_init', function(): void {
register_rest_field('page', 'servicos_api_formatados', [
'get_callback' => function( array $post_arr ): array {
$post_id = $post_arr['id'];
$linhas = get_field('blocos_servicos', $post_id);
if ( empty( $linhas ) || ! is_array( $linhas ) ) {
return [];
}
return array_map( function( array $linha, int $chave ): array {
return [
'posicao' => $chave + 1,
'ePar' => ( ( $chave + 1 ) % 2 === 0 ),
'titulo' => sanitize_text_field( $linha['titulo'] ?? '' ),
'texto' => wp_kses_post( $linha['descricao'] ?? '' ),
];
}, $linhas, array_keys( $linhas ) );
},
'schema' => [
'description' => 'Dados de serviços normalizados para frontend headless.',
'type' => 'array',
],
]);
});Modularizacao de componentes com get_template_part
Para manter ficheiros de tema limpos e reutilizáveis, separe a marcação de cartões individuais em partes de template, aproveitando o suporte nativo a argumentos em get_template_part():
<?php if ( have_rows('planos_precos') ) : ?>
<div class="precos-grelha-contentor">
<?php while ( have_rows('planos_precos') ) : the_row();
$pos = get_row_index();
$parametros = [
'indice' => $pos,
'e_par' => ( $pos % 2 === 0 ),
'nome' => get_sub_field('nome_plano'),
'preco' => get_sub_field('valor_preco'),
'funcoes' => get_sub_field('lista_recursos'),
];
get_template_part( 'template-parts/componentes/cartao-preco', null, $parametros );
?>
<?php endwhile; ?>
</div>
<?php endif; ?>Exemplo completo: tabela de precos com plano recomendado
Para demonstrar a convergência destas técnicas, apresentamos a implementação de uma tabela de preços interativa:
<?php if ( have_rows('planos_precos') ) :
$todos_planos = get_field('planos_precos');
$total_itens = is_array( $todos_planos ) ? count( $todos_planos ) : 0;
?>
<section class="tabela-precos-seccao" id="precos" aria-label="<?php esc_attr_e('Tabela de Preços', 'wppoland'); ?>">
<div class="precos-grelha">
<?php while ( have_rows('planos_precos') ) : the_row();
$i = get_row_index();
$nome = get_sub_field('nome_plano');
$preco = get_sub_field('valor_preco');
$e_bestseller = (bool) get_sub_field('destacar_plano');
$recursos = get_sub_field('lista_recursos');
$classes = [ 'cartao-preco', 'posicao-' . $i ];
if ( $e_bestseller ) {
$classes[] = 'cartao-recomendado';
}
if ( $i === 1 ) {
$classes[] = 'cartao-inicial';
}
if ( $i === $total_itens ) {
$classes[] = 'cartao-avancado';
}
$classes[] = ( $i % 2 === 0 ) ? 'tom-secundario' : 'tom-primario';
?>
<div class="<?php echo esc_attr( implode( ' ', $classes ) ); ?>">
<?php if ( $e_bestseller ) : ?>
<span class="etiqueta-destaque"><?php esc_html_e('Mais Popular', 'wppoland'); ?></span>
<?php endif; ?>
<h3 class="plano-titulo"><?php echo esc_html( $nome ); ?></h3>
<div class="plano-valor"><?php echo esc_html( $preco ); ?></div>
<?php if ( ! empty( $recursos ) && is_array( $recursos ) ) : ?>
<ul class="plano-vantagens">
<?php foreach ( $recursos as $item ) : ?>
<li><?php echo esc_html( $item['texto_recurso'] ?? '' ); ?></li>
<?php endforeach; ?>
</ul>
<?php endif; ?>
<div class="plano-acao">
<a href="#contacto" class="botao-aderir"><?php esc_html_e('Subscrever Plano', 'wppoland'); ?></a>
</div>
</div>
<?php endwhile; ?>
</div>
</section>
<?php endif; ?>Configuração CSS moderna utilizando CSS Grid e transições suaves:
.precos-grelha {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
gap: 2rem;
align-items: stretch;
}
.cartao-preco {
border-radius: 12px;
padding: 2rem;
background: #ffffff;
border: 1px solid #e2e8f0;
transition: transform 0.25s ease, box-shadow 0.25s ease;
}
.cartao-preco:hover {
transform: translateY(-4px);
box-shadow: 0 12px 24px -8px rgba(15, 23, 42, 0.12);
}
.cartao-preco.cartao-recomendado {
border: 2px solid #2563eb;
position: relative;
box-shadow: 0 20px 25px -5px rgba(37, 99, 235, 0.1);
}
.cartao-preco.tom-secundario {
background-color: #f8fafc;
}Diagnostico e resolucao de problemas comuns
- Loop infinito: Esquecer a chamada a
the_row()impede o avanço do ponteiro, fazendo com que a condiçãowhile( have_rows() )seja verdadeira eternamente, esgotando a memória do servidor. - Subcampos vazios por uso incorreto de get_field: Usar
get_field()em vez deget_sub_field()dentro do ciclo repeater resulta na leitura do post global em vez dos valores locais. - Erros fatais com count() no PHP 8: Em versões modernas do PHP, chamar
count()numa variável que não seja array resulta num TypeError fatal. Valide sempre a variável previamente comis_array(). - Seletores CSS que não funcionam: Plugins de carrossel ou frameworks visuais podem injetar elementos
<div>intermediários que quebram seletores:nth-child(). Adicione classes explícitas via PHP para contornar esta limitação.
Perguntas frequentes sobre ACF Repeater
Qual e a diferenca entre get_row_index e get_row_layout
O get_row_index comeca em 0 ou em 1
Como evitar lentidao na base de dados com o ACF Repeater
Gestao de repeaters aninhados e hierarquias de informacao
Em cenários corporativos exigentes, a modelação de dados requer frequentemente campos repetíveis multinível (nested repeaters), tais como departamentos de uma organização onde cada departamento gere os seus próprios membros e cargos.
<?php if ( have_rows('estrutura_departamentos') ) : ?>
<div class="organograma-departamentos">
<?php while ( have_rows('estrutura_departamentos') ) : the_row();
$indice_dept = get_row_index();
$nome_dept = get_sub_field('nome_departamento');
?>
<section class="departamento-bloco dept-<?php echo esc_attr( $indice_dept ); ?>">
<h2 class="departamento-titulo"><?php echo esc_html( $nome_dept ); ?></h2>
<?php if ( have_rows('colaboradores') ) : ?>
<ul class="colaboradores-lista">
<?php while ( have_rows('colaboradores') ) : the_row();
$indice_colab = get_row_index();
$nome_colab = get_sub_field('nome');
$cargo_colab = get_sub_field('cargo');
$classe_linha = ( $indice_colab % 2 === 0 ) ? 'item-linha-b' : 'item-linha-a';
?>
<li class="colaborador-card <?php echo esc_attr( $classe_linha ); ?>">
<strong><?php echo esc_html( $nome_colab ); ?></strong>
<span><?php echo esc_html( $cargo_colab ); ?></span>
</li>
<?php endwhile; ?>
</ul>
<?php endif; ?>
</section>
<?php endwhile; ?>
</div>
<?php endif; ?>Neste contexto aninhado, a chamada a get_row_index() dentro do ciclo colaboradores reflete exclusivamente o índice ordinal do colaborador dentro do departamento específico, simplificando a aplicação de classes zebradas locais sem interferir com os contadores globais.
Impacto na serializacao em Redis e gestao de memoria do servidor
Numa infraestrutura de alojamento de elevado tráfego com cache de objetos em Redis ou Memcached, a forma como o WordPress manipula metadados pode influenciar diretamente o consumo de memória do cluster.
Fatores de degradacao em repeaters extensos
- Sobrecarga de serializacao de metadados: A função nativa
update_postmeta_cache()carrega todos os registos de metadados do artigo numa única chave de cache serializada. Se um repeater tiver dezenas de linhas com imagens em alta resolução e campos de texto extensos, o objeto armazenado em Redis pode ultrapassar várias centenas de kilobytes. - Tempo de processamento no motor PHP: Cada linha percorrida pelo ACF necessita de mapear referências internas e filtros de formato. A avaliação de 100 subcampos consome ciclos preciosos de CPU do servidor web, aumentando o TTFB.
- Bloqueio de tabelas em operacoes de escrita: Durante a atualização de um artigo no painel de controlo, a remoção e reinserção massiva de dezenas de registos na tabela
wp_postmetapode provocar contenção de bloqueios em bases de dados MySQL concorridas.
A adoção de estratégias de fragment caching com Transients ou a migração para a arquitetura de blocos nativos Gutenberg representa uma solução definitiva para minimizar este impacto na infraestrutura.
Estruturas de grelha CSS modernas e design responsivo
No desenvolvimento de interfaces orientadas a desempenho, a combinação de classes utilitárias semânticas geradas no PHP com sistemas de CSS Grid permite alcançar layouts flexíveis que dispensam scripts pesados de JavaScript:
.servicos-grelha-cache {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
gap: 2rem;
margin: 2rem 0;
}
.cartao-par {
background-color: #f8fafc;
border-left: 4px solid #2563eb;
}
.cartao-impar {
background-color: #ffffff;
border-left: 4px solid #64748b;
}Ao aplicar estas regras, o navegador reorganiza automaticamente o número de colunas consoante a largura da janela de visualização (viewport), garantindo uma experiência de navegação estável e pontuações elevadas nas métricas Core Web Vitals (nomeadamente Cumulative Layout Shift e Largest Contentful Paint).
Medidas de seguranca e validacao de dados na renderizacao
A proteção contra vulnerabilidades de segurança exige atenção constante ao apresentar valores fornecidos pelos utilizadores no frontend:
- Higienização de texto: Envolva sempre textos simples em
esc_html()ouesc_attr()consoante o contexto em que são inseridos. - URLs e ligações externas: Utilize a função
esc_url()em todos os subcampos destinados a hiperligações e adicione os atributos de segurançarel="noopener noreferrer"para destinos externos. - Imagens anexadas: Recorra preferencialmente a identificadores numéricos de anexos em combinação com
wp_get_attachment_image(), assegurando o carregamento diferido nativo (loading="lazy").
Conclusao tecnica e lista de recomendacoes
A correta implementação de campos repetíveis no ACF equilibra simplicidade editorial com alto desempenho no frontend. Ao combinar a indexação nativa com boas práticas de CSS moderno e estratégias de cache, desenvolve soluções escaláveis e preparadas para ambientes de produção.
Recomendações fundamentais para o seu fluxo de desenvolvimento:
- Valide a existência de registos com
have_rows()e processe a iteração rigorosamente comthe_row(). - Aceda exclusivamente aos valores através de
get_sub_field(). - Aplique sanitização em todas as saídas com
esc_html(),esc_attr()ewp_kses_post(). - Mantenha estilos cosméticos no CSS com
:nth-child()e reserve a lógica PHP para alterações estruturais. - Proteja o desempenho da base de dados com o Transients API em listagens volumosas.
Necessita de apoio de engenharia de software para otimizar plataformas WordPress complexas ou criar blocos Gutenberg sob medida? Conheça os nossos serviços de desenvolvimento WordPress e soluções dedicadas de manutenção WordPress.







