How to Customize the Ultimate Member Welcome Email Template (welcome_email.php)
Override the Ultimate Member welcome_email.php at theme level, add role-specific emails, and customize registration confirmation messages.…
Complete reference for accessing WooCommerce order data programmatically - getter methods, get_data(), billing/shipping fields, iterating order items, and a practical custom admin column example. Updated for WooCommerce 7.1+ HPOS.
Accessing WooCommerce order data programmatically comes up constantly in custom development - whether you're building a custom order admin column, a webhook payload, a third-party integration, or a custom email notification. Since WooCommerce 3.0, the entire order data API changed: direct property access is gone, replaced by getter methods and the get_data() array. Here's the complete reference with practical examples.
Everything starts with wc_get_order(). Pass it an order ID and you get back a WC_Order object:
$order = wc_get_order( $order_id );
if ( ! $order ) {
return; // always check - returns false if ID is invalid
}
Inside order hooks like woocommerce_order_status_changed, the order object is passed directly as a parameter, so you don't need to call wc_get_order() again.
These are the getters you'll use most often on a WC_Order instance:
$order->get_id(); // Order ID (integer)
$order->get_status(); // e.g. 'pending', 'processing', 'completed'
$order->get_currency(); // e.g. 'CAD', 'USD'
$order->get_total(); // Order total (string, formatted as decimal)
$order->get_subtotal(); // Subtotal before tax/shipping
$order->get_discount_total(); // Total discount applied
$order->get_shipping_total(); // Shipping total
$order->get_total_tax(); // Total tax
$order->get_payment_method(); // Payment method ID e.g. 'stripe'
$order->get_payment_method_title(); // Human-readable e.g. 'Credit Card (Stripe)'
$order->get_date_created(); // WC_DateTime object
$order->get_date_modified(); // WC_DateTime object
$order->get_date_completed(); // WC_DateTime object (null if not completed)
$order->get_customer_id(); // WordPress user ID (0 for guests)
$order->get_user(); // WP_User object (false for guests)
$order->get_customer_ip_address(); // IP at time of order
$order->get_customer_note(); // Order note from customer
$order->get_transaction_id(); // Payment gateway transaction ID
$order->get_created_via(); // e.g. 'checkout', 'admin', 'rest-api'
// Billing
$order->get_billing_first_name();
$order->get_billing_last_name();
$order->get_billing_company();
$order->get_billing_email();
$order->get_billing_phone();
$order->get_billing_address_1();
$order->get_billing_address_2();
$order->get_billing_city();
$order->get_billing_state();
$order->get_billing_postcode();
$order->get_billing_country();
// Shipping
$order->get_shipping_first_name();
$order->get_shipping_last_name();
$order->get_shipping_company();
$order->get_shipping_address_1();
$order->get_shipping_address_2();
$order->get_shipping_city();
$order->get_shipping_state();
$order->get_shipping_postcode();
$order->get_shipping_country();
Use has_status() rather than comparing get_status() directly - it handles the wc- prefix automatically:
if ( $order->has_status( 'completed' ) ) {
// order is complete
}
// Check multiple statuses at once
if ( $order->has_status( array( 'processing', 'on-hold' ) ) ) {
// order needs attention
}
When you need to pass order data to a third-party integration or build a JSON payload, get_data() returns everything as a structured array - no need to call each getter individually:
$order_data = $order->get_data();
// Top-level keys available:
$order_data['id'];
$order_data['status'];
$order_data['currency'];
$order_data['total'];
$order_data['discount_total'];
$order_data['shipping_total'];
$order_data['total_tax'];
$order_data['customer_id'];
$order_data['payment_method'];
$order_data['payment_method_title'];
// Dates are WC_DateTime objects
$order_data['date_created']->date('Y-m-d H:i:s');
$order_data['date_created']->getTimestamp();
// Billing and shipping are nested arrays
$order_data['billing']['email'];
$order_data['billing']['phone'];
$order_data['shipping']['address_1'];
Each item in get_items() is a WC_Order_Item_Product object. This is where you access what was actually purchased:
foreach ( $order->get_items() as $item_id => $item ) {
$product = $item->get_product(); // WC_Product object
$product_id = $item->get_product_id();
$variation_id = $item->get_variation_id(); // 0 if not a variation
$item_name = $item->get_name();
$quantity = $item->get_quantity();
$line_total = $item->get_total(); // after discounts
$line_subtotal = $item->get_subtotal(); // before discounts
// From the product object
if ( $product ) {
$sku = $product->get_sku();
$product_price = $product->get_price();
$stock_qty = $product->get_stock_quantity();
$product_type = $product->get_type(); // 'simple', 'variable', etc.
}
// Item meta (custom fields on the line item)
$item_meta = $item->get_meta( 'your_meta_key' );
}
Other item types available via the $type parameter on get_items(): shipping, tax, coupon, fee.
// Get shipping items
foreach ( $order->get_items( 'shipping' ) as $shipping_item ) {
$method_title = $shipping_item->get_name();
$shipping_cost = $shipping_item->get_total();
}
// Get coupons applied
foreach ( $order->get_items( 'coupon' ) as $coupon_item ) {
$coupon_code = $coupon_item->get_name();
$coupon_discount = $coupon_item->get_discount();
}
Custom order meta added by plugins or your own code is accessible via get_meta():
// Get a single meta value
$custom_value = $order->get_meta( '_custom_field_key' );
// Get all meta as an array of WC_Meta_Data objects
$all_meta = $order->get_meta_data();
foreach ( $all_meta as $meta ) {
$meta_data = $meta->get_data();
// $meta_data['key'], $meta_data['value']
}
This is a common real-world use - adding a custom column to the WooCommerce orders list that shows data you frequently reference:
// Add the column header
add_filter( 'manage_woocommerce_page_wc-orders_columns', function( $columns ) {
$columns['customer_phone'] = 'Phone';
return $columns;
} );
// Populate the column
add_action( 'manage_woocommerce_page_wc-orders_custom_column', function( $column, $order ) {
if ( 'customer_phone' === $column ) {
echo esc_html( $order->get_billing_phone() );
}
}, 10, 2 );
Note: WooCommerce 7.1+ uses the woocommerce_page_wc-orders screen for the orders list (HPOS). For sites still using the legacy shop_order post type, replace woocommerce_page_wc-orders with edit-shop_order in both hooks.
If you're building a deeper WooCommerce integration - custom order workflows, third-party sync, or a tailored admin experience - I do this kind of work for clients across Toronto and Canada.