wc_get_orders and WC_Order_Query: Correct Order Queries in HPOS

wc_get_orders() and WC_Order_Query are the correct API for querying WooCommerce orders — they respect HPOS (High-Performance Order Storage) and work whether orders are stored in custom tables or post meta. Using get_posts() or WP_Query on shop_order directly breaks on HPOS-enabled stores.

Problem: A WooCommerce plugin or theme uses get_posts() with post_type='shop_order' to query orders — but after migrating to HPOS (High-Performance Order Storage), these queries bypass the new order tables and return incorrect or empty results.

Solution: Replace all order queries with wc_get_orders() or WC_Order_Query, which abstracts the storage backend. Check HPOS compatibility by running WooCommerce → Status → Tools → Verify database. Use wc_get_order() for single order lookups instead of get_post().

The examples below demonstrate the most common query patterns — filtering by status, customer, date range, and product — and show the equivalent WP_Query that should be replaced.

 'completed',
    'limit'    => 20,
    'orderby'  => 'date',
    'order'    => 'DESC',
    'return'   => 'objects', // or 'ids'
] );

// 2. Orders by a specific customer
$customer_orders = wc_get_orders( [
    'customer_id' => 42,
    'status'      => [ 'processing', 'on-hold' ],
    'limit'       => -1,   // all matching orders
] );

// 3. Orders in a date range
$orders_this_month = wc_get_orders( [
    'date_created' => '2024-09-01...2024-09-30',
    'status'       => 'completed',
    'limit'        => -1,
] );

// 4. Orders containing a specific product (by product ID)
$orders_for_product = wc_get_orders( [
    'limit'      => -1,
    'status'     => 'completed',
    'product_id' => 123,
] );

// 5. Paginated query with total count
$query = new WC_Order_Query( [
    'status'   => 'processing',
    'limit'    => 10,
    'paged'    => 2,
    'paginate' => true,  // returns object with ->orders, ->total, ->max_num_pages
] );
$result = $query->get_orders();
echo "Total processing orders: {$result->total}";

Read order data correctly via the HPOS-safe API:

post_title etc.
    $order_id      = $order->get_id();
    $status        = $order->get_status();           // without 'wc-' prefix
    $total         = $order->get_total();
    $customer_id   = $order->get_customer_id();
    $customer_note = $order->get_customer_note();
    $date_created  = $order->get_date_created();     // WC_DateTime object

    // Get order items
    foreach ( $order->get_items() as $item_id => $item ) {
        $product_id = $item->get_product_id();
        $qty        = $item->get_quantity();
        $line_total = $item->get_total();
    }

    // Get order meta (works on both HPOS and legacy)
    $tracking_number = $order->get_meta( '_tracking_number', true );

    // Save meta back
    $order->update_meta_data( '_tracking_number', 'TRK123456' );
    $order->save();
}

// ── WRONG — breaks on HPOS stores ──
// $orders = get_posts( [ 'post_type' => 'shop_order', ... ] );
// $order_post = get_post( $order_id );
// get_post_meta( $order_id, '_billing_email', true ); // unreliable on HPOS

NOTE: Enable HPOS in WooCommerce → Settings → Advanced → Features and run the migration wizard. After migration, always audit your codebase for get_posts( ['post_type' => 'shop_order'] ) calls and replace them with wc_get_orders().

Leave Comment

Your email address will not be published. Required fields are marked *