DesenvolvedorWordPress WhatsApp

Atributos e variações do WooCommerce via código

Cor, tamanho, voltagem: por trás de cada produto variável há uma engrenagem de atributos (globais ou locais) e variações que muita gente cria no clique e quebra no código. Aqui está como modelar isso programaticamente — do jeito certo, sem corromper o produto.

Precisa de desenvolvimento sob medida?

Atualizado em 19/06/2026

Um produto "Camiseta" que existe em três cores e quatro tamanhos não é um produto: é um produto variável com (até) doze variações, cada uma com seu próprio SKU, preço, estoque e imagem. O WooCommerce modela isso com uma hierarquia de objetos e dois tipos de atributo — globais e locais — que se comportam de formas bem diferentes. Quem só usa o painel raramente percebe a diferença; quem cria produtos por código, em importações ou integrações, precisa entendê-la a fundo, ou acaba com variações fantasmas, preços que não atualizam e taxonomias poluídas. Este artigo cobre a mecânica completa pela CRUD API.

Atributos globais × locais: a decisão que define tudo

Antes de uma linha de código, a escolha estrutural. O WooCommerce tem dois tipos de atributo:

A regra: se o mesmo conjunto de valores se repete entre produtos, ou se você quer filtrar/relatar por ele, use global. Use local só para um detalhe genuinamente único daquele item. Em lojas que crescem, o erro caro é criar tudo como local: depois não dá para filtrar por cor, e migrar para global exige reprocessar o catálogo.

AspectoAtributo global (pa_*)Atributo local (do produto)
NaturezaTaxonomia (pa_cor, pa_tamanho)Metadado do próprio produto
ValoresTermos (WP_Term) reutilizáveisTexto livre, separado por |
Reutilizável entre produtosSimNão
Filtros / navegação por camadasSimNão
Identificado no código porset_id() (ID do atributo)set_name() (rótulo em texto)
Valor em set_attributes()slug do termo (azul)o próprio texto (Azul)

A hierarquia de objetos: variável × variação

No lado dos produtos, três classes interessam aqui. O produto pai é um WC_Product_Variable: ele não tem preço próprio, mas guarda a lista de atributos (alguns marcados como "usados para variações") e funciona como guarda-chuva. Cada combinação vendável é um WC_Product_Variation — um filho com parent_id apontando para o variável, e com seu próprio preço, SKU, estoque e imagem.

WC_Product_VariableWC_Product_Variation
PapelProduto pai (guarda-chuva)Combinação vendável (filho)
post_typeproductproduct_variation
Tem preço próprio?Não (faixa derivada dos filhos)Sim
GuardaAtributos + quais variamValores escolhidos por atributo
Leitura agregadaget_available_variations(), get_variation_prices()getters individuais

Produtos não são HPOS. O HPOS (High-Performance Order Storage) reorganizou apenas os pedidos. Produtos e variações continuam sendo posts (product e product_variation) e os atributos globais continuam taxonomias. Mas a disciplina é a mesma: use a CRUD API, não get_post_meta(). Detalhes em produtos e pedidos via código.

Passo 1 — Criar o atributo global e seus termos

Um atributo global tem duas camadas: o registro do atributo (que cria a taxonomia pa_cor) e os termos dentro dela. Para registrar o atributo em si, use wc_create_attribute(); os termos são inseridos com wp_insert_term() na taxonomia pa_cor. Cheque sempre o retorno, porque wp_insert_term() devolve WP_Error se o termo já existir:

<?php
// 1. Registra o atributo global "Cor" -> cria a taxonomia pa_cor
//    (se ele já existir, wc_create_attribute devolve WP_Error)
$attribute_id = wc_create_attribute( array(
    'name'         => 'Cor',     // rótulo legível
    'slug'         => 'cor',     // vira a taxonomia pa_cor
    'type'         => 'select',
    'order_by'     => 'menu_order',
    'has_archives' => false,
) );

if ( is_wp_error( $attribute_id ) ) {
    // já existe? recupere o ID a partir do slug
    $attribute_id = wc_attribute_taxonomy_id_by_name( 'cor' );
}

// É preciso registrar a taxonomia antes de inserir termos na mesma request.
$taxonomy = 'pa_cor'; // prefixo pa_ é obrigatório
if ( ! taxonomy_exists( $taxonomy ) ) {
    register_taxonomy( $taxonomy, 'product', array( 'hierarchical' => false ) );
}

// 2. Insere os termos (valores) na taxonomia pa_cor
foreach ( array( 'Azul', 'Vermelho', 'Preto' ) as $valor ) {
    if ( ! term_exists( $valor, $taxonomy ) ) {
        $res = wp_insert_term( $valor, $taxonomy, array( 'slug' => sanitize_title( $valor ) ) );
        if ( is_wp_error( $res ) ) {
            error_log( 'Falha ao criar termo: ' . $res->get_error_message() );
        }
    }
}

O prefixo pa_ não é opcional nem decorativo: é como o WooCommerce reconhece uma taxonomia de atributo de produto. Inserir termos em uma taxonomia sem esse prefixo não os torna atributos. E lembre que, fora do fluxo administrativo, a taxonomia pa_cor pode ainda não estar registrada na requisição — registrá-la (ou chamar o código em init) evita o erro "Invalid taxonomy".

Passo 2 — Definir os atributos no produto variável

Com a taxonomia e os termos prontos, monte o produto pai. Cada atributo do produto é um objeto WC_Product_Attribute. Para um atributo global, você informa o ID (via set_id()) e a taxonomia em set_name(); para um local, deixa o ID em 0 e usa set_name() com o rótulo. O método decisivo é set_variation( true ): ele marca o atributo como "usado para variações" — sem isso, nenhuma variação aparece.

<?php
$product = new WC_Product_Variable();
$product->set_name( 'Camiseta Dev WP' );
$product->set_status( 'publish' );
$product->set_sku( 'DEVWP-CAM' );

// --- Atributo GLOBAL (pa_cor) ---
$attr_cor = new WC_Product_Attribute();
$attr_cor->set_id( wc_attribute_taxonomy_id_by_name( 'cor' ) ); // ID = global
$attr_cor->set_name( 'pa_cor' );                                 // a taxonomia
$attr_cor->set_options( array( 'azul', 'vermelho', 'preto' ) );  // SLUGS dos termos
$attr_cor->set_visible( true );
$attr_cor->set_variation( true ); // ESSENCIAL: usado para variações

// --- Atributo LOCAL (sem taxonomia) ---
$attr_estilo = new WC_Product_Attribute();
$attr_estilo->set_id( 0 );                 // 0 = local
$attr_estilo->set_name( 'Gola' );          // rótulo em texto
$attr_estilo->set_options( array( 'Redonda', 'V' ) ); // o próprio texto
$attr_estilo->set_visible( true );
$attr_estilo->set_variation( true );

// Atribui os dois ao produto e salva
$product->set_attributes( array( $attr_cor, $attr_estilo ) );
$parent_id = $product->save(); // retorna o ID do produto pai

Repare na assimetria que confunde muita gente: em set_options(), o atributo global recebe os slugs dos termos (azul), enquanto o local recebe o texto exibido (Redonda). Misturar os dois — passar "Azul" para o global, por exemplo — faz a opção não casar com nenhum termo e a variação silenciosamente não funciona.

Passo 3 — Criar uma variação

Cada combinação é um WC_Product_Variation. Você define o parent_id, passa os atributos escolhidos (no formato taxonomia => slug para globais; rótulo => texto para locais) e seus dados de venda. Como sempre, nada é gravado até save():

<?php
$variation = new WC_Product_Variation();
$variation->set_parent_id( $parent_id );

// Atributos escolhidos por ESTA variação.
// Global: chave 'pa_cor' + SLUG do termo. Local: chave do rótulo (em minúsculas) + texto.
$variation->set_attributes( array(
    'pa_cor' => 'azul',
    'gola'   => 'Redonda',
) );

$variation->set_regular_price( '79.90' );
$variation->set_sku( 'DEVWP-CAM-AZ-R' );
$variation->set_manage_stock( true );
$variation->set_stock_quantity( 25 );
$variation->set_status( 'publish' );

$variation_id = $variation->save(); // SEM isto, a variação não existe

// Reconcilia o produto pai (faixa de preços, "em estoque", etc.)
WC_Product_Variable::sync( $parent_id );

Para deixar uma variação válida para "qualquer" valor de um atributo (o "any" do painel), passe string vazia naquele atributo: 'pa_cor' => ''. A correspondência prioriza variações com valor explícito; só recai nas de valor vazio quando nenhuma específica bate. Combinar as duas estratégias no mesmo produto sem critério gera ambiguidade — defina o conjunto de variações de forma a cobrir as combinações sem sobreposição.

Passo 4 — Ler atributos e variações

Na leitura, o ponto de entrada é sempre wc_get_product( $id ). Para inspecionar os atributos do produto, use get_attributes(). Para listar as combinações exibíveis de um variável, get_available_variations() devolve um array rico (atributos resolvidos, preço, estoque, imagem) — ótimo para alimentar um seletor de variação, mas pesado:

<?php
$variable = wc_get_product( $parent_id );

if ( $variable && $variable->is_type( 'variable' ) ) {

    // (a) Atributos do produto (globais e locais)
    foreach ( $variable->get_attributes() as $taxonomy => $attribute ) {
        echo esc_html( $attribute->get_name() ) . ': '
           . esc_html( implode( ', ', $attribute->get_options() ) ) . '<br>';
    }

    // (b) Combinações exibíveis (pesado — evite em loops de listagem)
    foreach ( $variable->get_available_variations() as $v ) {
        printf(
            '#%d — %s — R$ %s%s',
            (int) $v['variation_id'],
            esc_html( implode( ' / ', $v['attributes'] ) ),
            esc_html( $v['display_price'] ),
            '<br>'
        );
    }

    // (c) Preços com cache, indexados por ID da variação (mais leve)
    $precos = $variable->get_variation_prices( true );
    // $precos['price'] = [ variation_id => preco_ativo, ... ]
}

Em listagens e grids, prefira get_variation_prices(): ele é cacheado e devolve só os números necessários para montar a faixa "de R$ X a R$ Y". Reserve get_available_variations() para a página do produto, onde você realmente precisa de imagem, estoque e atributos por combinação.

Quando você já sabe a combinação escolhida e quer o ID da variação correspondente (por exemplo, ao montar um pedido por código), use o WC_Data_Store de produtos com find_matching_product_variation() — é assim que o próprio WooCommerce resolve a seleção do cliente, respeitando inclusive os valores "any":

<?php
$variable   = wc_get_product( $parent_id );
$data_store = WC_Data_Store::load( 'product' );

// Combinação escolhida: as chaves DEVEM vir prefixadas com "attribute_"
$match_id = $data_store->find_matching_product_variation( $variable, array(
    'attribute_pa_cor' => 'azul',
    'attribute_gola'   => 'Redonda',
) );

if ( $match_id ) {
    $variation = wc_get_product( $match_id ); // WC_Product_Variation
    echo esc_html( $variation->get_sku() . ' — R$ ' . $variation->get_price() );
} else {
    echo 'Nenhuma variação corresponde a essa combinação.';
}

Repare que aqui as chaves levam o prefixo attribute_ (attribute_pa_cor), diferente do set_attributes() da criação — é uma das inconsistências da API que mais geram erro silencioso. O retorno é o ID da variação (ou 0 se nada casar).

Armadilhas comuns

Boas práticas

Modelar atributos e variações por código é o coração de qualquer importação de catálogo, integração com ERP ou ferramenta de criação em massa de produtos. Combine isso com os hooks do WooCommerce para customizar a exibição e com a CRUD API de produtos e pedidos para o resto do fluxo. Se a sua loja precisa de importação ou reorganização de catálogo feita com segurança, conheça os serviços de suporte e manutenção WooCommerce ou veja os demais artigos de desenvolvimento.

Precisa criar ou importar produtos variáveis por código?

Desenvolvo importações de catálogo, atributos globais e variações WooCommerce pela CRUD API, sem corromper o produto. Me chame no WhatsApp.

WhatsApp: (43) 99932-9697

ou e-mail: [email protected]

Perguntas frequentes

Qual a diferença entre atributo global e atributo local no WooCommerce?

O atributo global é uma taxonomia (pa_cor, pa_tamanho) cadastrada em Produtos › Atributos e reutilizável por toda a loja: os valores são termos compartilhados entre produtos, o que permite filtros por atributo, widgets de camadas e relatórios consistentes. O atributo local existe só dentro de um produto, é digitado livremente e não vira taxonomia — bom para um detalhe único que nunca se repete. Regra prática: se mais de um produto usa o mesmo conjunto de valores, ou se você quer filtrar por ele, use global.

Por que minha variação não aparece na loja depois de criá-la por código?

As três causas mais comuns: (1) você esqueceu de chamar $variation->save() — os setters só mexem no objeto em memória; (2) o atributo não foi marcado como set_variation( true ) no produto pai, então o WooCommerce não o considera "usado para variações"; (3) o valor passado em set_attributes() não corresponde a um termo válido do atributo global (em pa_cor deve ser o slug do termo, como azul, não o nome "Azul"). Depois de tudo, vale chamar WC_Product_Variable::sync( $parent_id ) para reconciliar preços e estado do pai.

O que significa o valor vazio ("any") em um atributo de variação?

Uma variação pode não fixar um valor para determinado atributo — no painel isso aparece como "Qualquer Cor", e na CRUD API corresponde a uma string vazia naquele atributo. Significa que a variação atende a qualquer opção daquele atributo. O WooCommerce casa a escolha do cliente primeiro com variações que têm valor explícito e, se nenhuma bater, recorre às de valor "any". Misturar variações específicas e "any" sem critério gera correspondências ambíguas; defina-as com cuidado.

Atributos e produtos usam HPOS como os pedidos?

Não. O HPOS (High-Performance Order Storage) afeta apenas o armazenamento de pedidos. Produtos continuam sendo posts (post_type product e product_variation) e os atributos globais continuam sendo taxonomias (pa_*) com seus termos. Mesmo assim, a recomendação é idêntica à dos pedidos: leia e grave pela CRUD API (wc_get_product(), getters/setters, save()) em vez de mexer em wp_postmeta direto. Veja produtos e pedidos via código.

Como leio o preço de uma variação específica sem instanciar cada uma?

No produto variável, $variable->get_variation_prices( true ) devolve arrays associativos (price, regular_price, sale_price) indexados pelo ID da variação, já com cache. É bem mais barato que iterar e instanciar cada WC_Product_Variation só para ler preço. Para o conjunto completo de combinações exibíveis (com imagem, estoque e atributos resolvidos), use get_available_variations() — porém ele é mais pesado, então evite chamá-lo em loops de listagem.

Centenas de variações deixam a loja lenta — o que fazer?

Sim, esse é um gargalo clássico. Cada variação é um post (product_variation) com seus metadados; produtos com 3-4 atributos de muitos valores geram explosão combinatória (10×10×5 = 500 variações). Mitigações: não crie combinações que você não vende; prefira get_variation_prices() a get_available_variations() em listagens; mantenha o cache de objeto ativo; e considere desmembrar em produtos separados quando os eixos forem realmente independentes. Veja WooCommerce lento.

Referências oficiais

  1. WooCommerce — Variable Product
  2. WooCommerce — Managing Product Taxonomies
  3. WooCommerce — Developer Documentation
  4. WooCommerce — Documentation
Tem alguma dúvida? Ficarei feliz em ajudar. Clique Aqui