DesenvolvedorWordPress WhatsApp

Produtos e pedidos no WooCommerce via código (CRUD)

Ler e gravar produtos e pedidos pelo banco direto é tentador — e errado. O WooCommerce expõe uma CRUD API de objetos que abstrai onde os dados ficam, e com o HPOS isso deixou de ser recomendação para virar obrigação.

Precisa de desenvolvimento sob medida?

Atualizado em 19/06/2026

Quem vem do WordPress "puro" tende a tratar produtos e pedidos do WooCommerce como posts: get_post() para carregar, get_post_meta() para ler o preço, update_post_meta() para gravar. Funcionou por anos — mas é a forma errada, e hoje pode simplesmente não funcionar. O WooCommerce fornece uma CRUD API de objetos que você deve usar no lugar: você pede um produto ou pedido, lê e altera por métodos (getters e setters) e salva, sem nunca saber em qual tabela aquilo está guardado. Este artigo mostra o uso correto e explica por que o HPOS tornou essa API inegociável para pedidos.

Data stores: a camada que abstrai o armazenamento

A peça central é o conceito de data store. Cada objeto do WooCommerce (WC_Product, WC_Order, etc.) não fala diretamente com o banco: ele delega a leitura e a escrita a uma classe de data store. Quando você chama $product->save(), o objeto pergunta ao data store "grave-me" — e é o data store que decide o como e o onde.

Essa indireção parece burocracia, mas é exatamente o que deu ao WooCommerce a liberdade de mudar o armazenamento de pedidos sem quebrar o ecossistema. Se o seu código usa $order->get_total(), ele continua correto independentemente de o pedido estar em wp_postmeta ou na tabela wc_orders. Se o seu código usa get_post_meta( $order_id, '_order_total', true ), ele depende de um detalhe interno — e quebra quando esse detalhe muda.

Produtos: ler, alterar e salvar

O ponto de entrada é wc_get_product( $id ), que devolve um objeto WC_Product (ou false se o ID não for um produto). A partir dele, getters retornam dados e setters os alteram em memória; nada vai ao banco até o save():

<?php
$product = wc_get_product( 123 );

if ( $product ) {
    // Leitura (getters)
    $nome      = $product->get_name();
    $preco     = $product->get_price();
    $estoque   = $product->get_stock_quantity();

    // Alteração (setters) — ainda só em memória
    $product->set_price( 149.90 );
    $product->set_regular_price( 149.90 );
    $product->set_stock_quantity( 25 );

    // Persiste TODAS as mudanças de uma vez
    $product->save();
}

Atenção a dois pares de preços: set_regular_price() define o preço cheio e set_sale_price() o promocional; set_price() ajusta o preço "ativo" (o que aparece). Para uma mudança definitiva, atualize o regular price — caso contrário, um recálculo futuro pode sobrescrever o que você gravou só em price.

Pedidos: carregar e iterar os itens

Para pedidos, o ponto de entrada é wc_get_order( $id ), que devolve um objeto WC_Order. Os itens do pedido não são propriedades soltas: você os obtém com $order->get_items() e itera, lendo nome e quantidade de cada linha:

<?php
$order = wc_get_order( 456 );

if ( $order ) {
    $total  = $order->get_total();
    $status = $order->get_status();
    $email  = $order->get_billing_email();

    // Itens de linha do pedido
    foreach ( $order->get_items() as $item_id => $item ) {
        $nome       = $item->get_name();
        $quantidade = $item->get_quantity();
        $subtotal   = $item->get_total();

        echo esc_html( $quantidade . 'x ' . $nome ) . '<br>';
    }

    // Mudar status pela API (dispara os hooks e e-mails corretos)
    $order->update_status( 'completed', 'Concluído via integração.' );
}

Prefira $order->update_status() a setar o status na mão: ele dispara os hooks de transição (como woocommerce_order_status_completed), envia os e-mails e registra a nota — comportamento que você perderia gravando o campo cru.

Consultando pedidos com wc_get_orders()

Para buscar vários pedidos, a função correta é wc_get_orders( $args ). Ela aceita argumentos de alto nível e funciona tanto no armazenamento legado quanto no HPOS, traduzindo a consulta para a tabela certa. É a substituta direta de qualquer WP_Query com post_type => 'shop_order':

<?php
$orders = wc_get_orders( array(
    'status'       => array( 'processing', 'on-hold' ),
    'limit'        => 20,
    'orderby'      => 'date',
    'order'        => 'DESC',
    'date_created' => '>' . ( time() - 30 * DAY_IN_SECONDS ), // últimos 30 dias
    'customer'     => '[email protected]',
) );

foreach ( $orders as $order ) {
    printf(
        'Pedido #%d — %s — R$ %s%s',
        $order->get_id(),
        esc_html( $order->get_status() ),
        esc_html( $order->get_total() ),
        '<br>'
    );
}

Para retornar apenas IDs (mais leve em listagens grandes), passe 'return' => 'ids'. Para produtos, o equivalente é wc_get_products( $args ), com a mesma filosofia.

Criando um produto simples por código

Criar um produto segue a mesma lógica: instancie a classe, use os setters e salve. save() retorna o ID do produto recém-criado:

<?php
$product = new WC_Product_Simple();

$product->set_name( 'Camiseta Dev WP' );
$product->set_status( 'publish' );
$product->set_regular_price( 79.90 );
$product->set_sku( 'DEVWP-CAM-001' );
$product->set_manage_stock( true );
$product->set_stock_quantity( 50 );
$product->set_description( 'Camiseta 100% algodão.' );

$product_id = $product->save(); // retorna o ID criado

Para variações, o caminho seria WC_Product_Variable + WC_Product_Variation; a mecânica de getters/setters/save() é idêntica.

HPOS: por que a CRUD API virou obrigatória

O HPOS (High-Performance Order Storage) é a mudança mais importante recente para quem programa pedidos. Historicamente, cada pedido era um post do tipo shop_order em wp_posts, com seus dados espalhados por wp_postmeta. Isso não escala: em lojas movimentadas, wp_postmeta incha e as consultas de pedido competem com todo o resto do site.

O HPOS move os pedidos para tabelas dedicadas (wc_orders e relacionadas), otimizadas para a estrutura de um pedido. O efeito colateral é direto e severo: pedidos deixam de ser posts. Logo:

A CRUD API (wc_get_order, wc_get_orders, $order->get_meta()) continua funcionando porque pergunta ao data store, não ao banco. Por isso a regra: todo acesso a pedidos passa pela CRUD API. E o seu plugin precisa declarar compatibilidade com o HPOS, ou o WooCommerce o tratará como incompatível:

<?php
use Automattic\WooCommerce\Utilities\FeaturesUtil;

add_action( 'before_woocommerce_init', function () {
    if ( class_exists( FeaturesUtil::class ) ) {
        FeaturesUtil::declare_compatibility(
            'custom_order_tables', // a feature do HPOS
            __FILE__,              // arquivo principal do plugin
            true                   // compatível
        );
    }
} );

Migrar uma loja para o HPOS (e auditar plugins que ainda usam get_post_meta() em pedidos) é parte do trabalho de modernização — veja migrar loja WooCommerce.

O que usar e o que NÃO usar

TarefaUse (CRUD API)NÃO use
Carregar um produtowc_get_product( $id )get_post( $id )
Ler o preço$product->get_price()get_post_meta(..., '_price')
Alterar e salvar produtosetters + $product->save()update_post_meta()
Carregar um pedidowc_get_order( $id )get_post( $id )
Ler dado do pedido$order->get_total(), $order->get_meta()get_post_meta() (não funciona no HPOS)
Iterar itens do pedido$order->get_items()Consultar woocommerce_order_items na mão
Mudar status do pedido$order->update_status()Setar o status cru sem disparar hooks
Buscar vários pedidoswc_get_orders( $args )WP_Query( post_type=shop_order )
Buscar vários produtoswc_get_products( $args )get_posts( post_type=product )

Resumo prático

Manipular dados pela CRUD API e expor comportamento pelos hooks do WooCommerce são as duas metades de qualquer desenvolvimento sério para a plataforma. Se a sua loja precisa de integrações, importações em massa ou de migração para o HPOS feita com segurança, conheça os serviços de suporte e manutenção WooCommerce ou veja os demais artigos de desenvolvimento.

Precisa manipular produtos e pedidos por código com segurança?

Desenvolvo integrações, importações e automações WooCommerce usando a CRUD API e compatíveis com HPOS. Me chame no WhatsApp.

WhatsApp: (43) 99932-9697

ou e-mail: [email protected]

Perguntas frequentes

Por que não usar get_post_meta() para ler dados de produtos e pedidos?

Porque você acopla o seu código ao onde os dados estão guardados, em vez de pedir o que você quer. A CRUD API (wc_get_product(), wc_get_order()) abstrai a camada de armazenamento por trás dos data stores. Com pedidos isso virou crítico: no HPOS eles não ficam mais em wp_posts/wp_postmeta, então get_post_meta() simplesmente não enxerga os dados. Já getters como $order->get_total() funcionam independentemente de onde o pedido vive.

O que são data stores no WooCommerce?

São as classes que isolam a lógica de persistência dos objetos. Quando você chama $product->save(), o objeto não escreve no banco diretamente: ele delega a um data store, que decide como e onde gravar. Essa indireção é o que permitiu ao WooCommerce trocar o armazenamento de pedidos (do modelo de posts para tabelas próprias no HPOS) sem que o seu código que usa a CRUD API precise mudar.

O que é o HPOS (High-Performance Order Storage)?

É o armazenamento de pedidos em tabelas dedicadas (wc_orders e relacionadas), em vez de reaproveitar wp_posts/wp_postmeta. Ganha-se desempenho em consultas e escrita, porque os pedidos deixam de competir com posts e metadados de todo o site. A consequência prática para o desenvolvedor: pedidos não são mais posts, então funções como get_post(), WP_Query e get_post_meta() não servem para pedidos — use a CRUD API.

Como deixo meu plugin compatível com HPOS?

Declarando compatibilidade no hook before_woocommerce_init com FeaturesUtil::declare_compatibility( 'custom_order_tables', __FILE__, true ) e, claro, acessando pedidos somente pela CRUD API. Sem essa declaração, o WooCommerce marca o seu plugin como incompatível e pode desativar o HPOS. Se você ainda usa get_post_meta() em pedidos, é aí que precisa migrar para $order->get_meta().

wc_get_orders() substitui WP_Query para pedidos?

Sim, e deve. wc_get_orders( $args ) é a forma agnóstica de consultar pedidos: ela funciona tanto no armazenamento legado quanto no HPOS, traduzindo os argumentos para a tabela correta. WP_Query com post_type => 'shop_order' só funciona enquanto os pedidos forem posts — com HPOS ativo, ela não retorna nada. Para produtos, a consulta equivalente é wc_get_products( $args ).

Devo chamar save() depois de cada setter?

Não. Os setters (set_price(), set_stock_quantity(), etc.) apenas alteram o objeto em memória. O save() é que persiste tudo de uma vez no data store. Faça todas as alterações e chame $product->save() (ou $order->save()) uma única vez ao final — é mais eficiente e evita escritas parciais.

Referências oficiais

  1. WooCommerce — Developer Documentation
  2. WooCommerce — Documentation
  3. WordPress.org — Code Reference
Tem alguma dúvida? Ficarei feliz em ajudar. Clique Aqui