woocommerce-data
WooCommerce Data — Orders, Products & APIs
Reference for HPOS-compatible data access, product/customer CRUD, Store API, and REST API integration.
1. HPOS (High-Performance Order Storage)
Since WooCommerce 8.2, HPOS is the default order storage for new installs.
Orders are stored in custom tables, not wp_postmeta.
Accessing Order Data (HPOS-Compatible)
// Always use CRUD methods — never direct SQL or get_post_meta().
$order = wc_get_order( $order_id );
// Read.
$total = $order->get_total();
$status = $order->get_status();
$billing = $order->get_billing_email();
$meta = $order->get_meta( '_mce_transaction_id' );
// Write.
$order->set_status( 'completed' );
$order->update_meta_data( '_mce_transaction_id', 'txn_123' );
$order->save();
Querying Orders
// Use wc_get_orders() — works with both HPOS and legacy storage.
$orders = wc_get_orders( array(
'status' => 'processing',
'limit' => 50,
'orderby' => 'date',
'order' => 'DESC',
'meta_query' => array(
array(
'key' => '_mce_synced',
'value' => 'no',
),
),
) );
Declare HPOS Compatibility
add_action( 'before_woocommerce_init', function (): void {
if ( class_exists( '\Automattic\WooCommerce\Utilities\FeaturesUtil' ) ) {
\Automattic\WooCommerce\Utilities\FeaturesUtil::declare_compatibility(
'custom_order_tables', __FILE__, true
);
}
} );
What NOT to Do
| Deprecated Pattern | HPOS-Compatible Alternative |
|---|---|
get_post_meta( $order_id, '_key', true ) |
$order->get_meta( '_key' ) |
update_post_meta( $order_id, '_key', $v ) |
$order->update_meta_data( '_key', $v ); $order->save(); |
new WP_Query( ['post_type'=>'shop_order'] ) |
wc_get_orders( [...] ) |
Direct SQL on wp_posts/wp_postmeta |
Use OrdersTableDataStore or CRUD methods |
get_post( $order_id ) |
wc_get_order( $order_id ) |
2. Product CRUD
// Read.
$product = wc_get_product( $product_id );
$name = $product->get_name();
$price = $product->get_price();
$sku = $product->get_sku();
$stock = $product->get_stock_quantity();
$type = $product->get_type(); // simple, variable, grouped, external.
$meta = $product->get_meta( '_mce_custom' );
// Write.
$product->set_regular_price( '29.99' );
$product->update_meta_data( '_mce_custom', 'value' );
$product->save();
Product Types
| Class | Type | Use Case |
|---|---|---|
WC_Product_Simple |
simple |
Standard product |
WC_Product_Variable |
variable |
Product with variations |
WC_Product_Variation |
variation |
Single variation of a variable |
WC_Product_Grouped |
grouped |
Collection of simple products |
WC_Product_External |
external |
Affiliate / external product |
3. Customer CRUD
$customer = new WC_Customer( $user_id );
$email = $customer->get_email();
$orders = $customer->get_order_count();
$spent = $customer->get_total_spent();
$customer->update_meta_data( '_mce_loyalty', 'gold' );
$customer->save();
4. Data Integrity
Always verify entity state before deletion or modification:
// Verify order status before deletion.
$order = wc_get_order( $order_id );
if ( ! $order || ! in_array( $order->get_status(), array( 'draft', 'checkout-draft' ), true ) ) {
return false; // Don't delete non-draft orders.
}
$order->delete( true );
// Verify ownership in batch operations.
foreach ( $order_ids as $id ) {
$order = wc_get_order( $id );
if ( $order && (int) $order->get_customer_id() === get_current_user_id() ) {
$order->delete( true );
}
}
Checklist: entity exists → correct state → correct owner → race-condition safe → check return value of delete/save.
5. DI Container
WooCommerce src/ classes use dependency injection:
$instance = wc_get_container()->get( \Automattic\WooCommerce\Internal\SomeClass::class );
Internal classes (src/Internal/) are private API — only rely on documented APIs.
6. Store API (Cart & Checkout)
The Store API is an unauthenticated public REST API for customer-facing cart, checkout, and product functionality.
Key Endpoints
| Method | Endpoint | Purpose |
|---|---|---|
GET |
/wc/store/v1/products |
List products |
GET |
/wc/store/v1/cart |
Get current cart |
POST |
/wc/store/v1/cart/add-item |
Add item to cart |
POST |
/wc/store/v1/checkout |
Place order |
PUT |
/wc/store/v1/checkout |
Update order (9.8+) |
Extending Store API
Use ExtendSchema to add custom data to Store API responses:
use Automattic\WooCommerce\StoreApi\StoreApi;
use Automattic\WooCommerce\StoreApi\Schemas\V1\CartItemSchema;
add_action( 'woocommerce_blocks_loaded', function (): void {
$extend = StoreApi::container()->get(
\Automattic\WooCommerce\StoreApi\Schemas\ExtendSchema::class
);
$extend->register_endpoint_data( array(
'endpoint' => CartItemSchema::IDENTIFIER,
'namespace' => 'my-extension',
'data_callback' => function ( array $cart_item ): array {
return array(
'custom_label' => get_post_meta( $cart_item['product_id'], '_mce_label', true ),
);
},
'schema_callback' => function (): array {
return array(
'custom_label' => array(
'type' => 'string',
'description' => 'Custom product label',
'readonly' => true,
),
);
},
) );
} );
7. WooCommerce REST API
The REST API is authenticated (consumer key + secret) for admin/back-office operations.
# Generate keys at WooCommerce > Settings > Advanced > REST API.
curl https://example.com/wp-json/wc/v3/products -u ck_xxx:cs_xxx
Namespaces
| Namespace | Purpose |
|---|---|
wc/v3 |
Core CRUD (products, orders, customers) |
wc/store/v1 |
Store API (unauthenticated, cart/checkout) |
wc-admin |
Admin analytics and reports |
wc-analytics |
Analytics data |
Common Endpoints
| Endpoint | Methods | Description |
|---|---|---|
/wc/v3/products |
GET, POST | Products CRUD |
/wc/v3/orders |
GET, POST | Orders CRUD |
/wc/v3/customers |
GET, POST | Customers CRUD |
/wc/v3/coupons |
GET, POST | Coupons CRUD |
/wc/v3/reports |
GET | Sales reports |
/wc/v3/webhooks |
GET, POST | Webhook management |
8. Common Mistakes
| Mistake | Fix |
|---|---|
Using get_post_meta() for order data |
Use $order->get_meta() — required for HPOS |
new WP_Query(['post_type'=>'shop_order']) |
Use wc_get_orders() — works with HPOS and legacy |
| Not declaring HPOS compatibility | Add FeaturesUtil::declare_compatibility('custom_order_tables', ...) |
Calling $order->set_status() without save() |
CRUD changes are only persisted after save() |
| Direct SQL queries on order tables | Use WC CRUD or wc_get_orders() |
Using $woocommerce global instead of WC() |
Use the WC() function — it's the singleton accessor |
| Deleting orders without verifying status | Check $order->get_status() before $order->delete() |
| Batch operations without per-item validation | Verify each item exists, has correct state and correct owner |
Related Skills
- woocommerce-setup — Extension architecture, plugin headers, FeaturesUtil
- woocommerce-payments — Payment gateways, block checkout integration
- woocommerce-hooks — Order lifecycle hooks, cart hooks, product hooks
More from peixotorms/odinlayer-skills
elementor-development
Use when building Elementor addons, creating custom widgets, or managing Elementor components. Covers Widget_Base class (get_name, get_title, get_icon, register_controls, render, content_template), widget registration via elementor/widgets/register hook, addon structure and plugin header, wp_enqueue_script for widget assets, get_script_depends, get_style_depends, inline editing toolbars, custom widget categories, manager registration (register/unregister), selector tokens ({{WRAPPER}}), deprecation handling, and Elementor CLI commands.
65elementor-hooks
Use when hooking into Elementor lifecycle events, injecting controls, filtering widget output, or using the JS APIs. Covers elementor/init, elementor/element/before_section_end, elementor/element/after_section_end, elementor/widget/render_content filter, elementor/frontend/after_enqueue_styles, frontend JS hooks (elementorFrontend.hooks, frontend/element_ready), editor JS hooks (elementor.hooks), $e.commands API ($e.run, $e.commands.register), $e.routes, $e.hooks (registerUIBefore, registerUIAfter), control injection patterns, CSS file hooks, forms hooks (Pro), and query filters.
26elementor-themes
Use when building Elementor-compatible themes, registering theme locations, creating dynamic tags, or extending the Finder. Covers register_location, theme_builder locations, elementor_theme_do_location, Theme_Document and theme conditions, Tag_Base for dynamic tags (register_tag, get_value, render), Finder extension (Category_Base, register via elementor/finder/register), Context_Menu customization (elements/context-menu/groups filter), Hello Elementor theme (elementor-hello-theme, hello_elementor_* filters), and hosting page cache integration hooks.
25elementor-controls
Use when adding controls to Elementor widgets, creating custom controls, or referencing control type parameters. Covers add_control with types (TEXT, SELECT, SLIDER, COLOR, MEDIA, REPEATER, CHOOSE, NUMBER, SWITCHER, URL, ICONS), TYPOGRAPHY and BACKGROUND group controls, BORDER, BOX_SHADOW group controls, add_responsive_control, add_group_control, CSS selectors ({{WRAPPER}}, {{VALUE}}), condition and conditions for conditional display, dynamic content tags, POPOVER_TOGGLE, and global styles integration.
16elementor-forms
Use when creating custom Elementor form actions, custom form field types, form validation, or processing form submissions. Covers Elementor Pro forms (ElementorPro\Modules\Forms), Action_Base (get_name, get_label, run, register_settings_section, on_export), after_submit processing, Field_Base (field_type, render field HTML, validation callback, update_controls), content_template for editor preview, form action registration, export_type handling, update_record patterns, elementor_pro/forms/validation hook, email filters, and webhook response handling.
12flyonui
Use when building with FlyonUI — Tailwind CSS component library with CSS classes and optional JS plugins. Covers CSS component classes, JS plugin system (accordion, carousel, collapse, combobox, datatable, dropdown, select, tabs, tooltip, etc.), theming, installation, class reference, and plugin initialization via MCP tools.
9