Skip to content
Back to skills

Woo Data Stores

ASecurity

Work with WooCommerce CRUD data stores — WC_Product, WC_Order, WC_Customer, WC_Coupon data objects, custom data stores, HPOS migration, and getters/setters. Use when creating or modifying WooCommerce data objects or implementing custom data stores.

  • 39 stars
  • 0 votes
  • 0 copies
  • 2 views
  • Added May 29, 2026
ai-agentsgophpsqlgitdatabasefrontendperformancedocumentation

Security analysis

A100/100

Scanned September 22, 2026

npx -y skills add OrcaQubits/agentic-commerce-skills-plugins --skill woo-data-stores --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Woo Data Stores?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Woo Data Stores
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/orcaqubits-woo-data-stores/badge)](https://www.skillsdirectory.com/skills/orcaqubits-woo-data-stores)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: woo-data-stores
description: >
  Work with WooCommerce CRUD data stores — WC_Product, WC_Order, WC_Customer,
  WC_Coupon data objects, custom data stores, HPOS migration, and
  getters/setters. Use when creating or modifying WooCommerce data objects or
  implementing custom data stores.
---

# WooCommerce CRUD & Data Stores

## Before writing code

**Fetch live docs**:
1. Web-search `site:developer.woocommerce.com crud data stores` for CRUD documentation
2. Web-search `site:github.com woocommerce woocommerce HPOS` for HPOS architecture
3. Fetch `https://github.com/woocommerce/woocommerce/wiki` for architecture decisions

## CRUD Architecture

### Data Objects

WooCommerce's core entities extend `WC_Data`:
- `WC_Product` — products (and subtypes: `WC_Product_Simple`, `WC_Product_Variable`, etc.)
- `WC_Order` — orders (and `WC_Order_Refund`)
- `WC_Customer` — customer profiles
- `WC_Coupon` — discount coupons
- `WC_Shipping_Zone` — shipping zones

### WC_Data Base Class

All CRUD objects inherit from `WC_Data`:
- `$data` — array of core properties with defaults
- `$meta_data` — array of `WC_Meta_Data` objects
- `$id` — object ID
- `$object_type` — string identifier (e.g., 'product', 'order')
- `$data_store` — the `WC_Data_Store` instance handling persistence

### Getters and Setters

- `get_{prop}( $context = 'view' )` — retrieve a property
  - `'view'` context — runs `woocommerce_{object_type}_get_{prop}` filter (for display)
  - `'edit'` context — returns raw stored value (for forms/admin)
- `set_{prop}( $value )` — set a property (in memory, not persisted until `save()`)
- `save()` — persist to database (calls data store's `create()` or `update()`)
- `delete( $force_delete )` — remove from database

### Meta Data

- `get_meta( $key, $single, $context )` — get meta value
- `update_meta_data( $key, $value, $meta_id )` — set meta (in memory)
- `delete_meta_data( $key )` — mark meta for deletion
- `save_meta_data()` — persist meta changes (called automatically by `save()`)

## Data Stores

### What Data Stores Do

Data stores abstract the persistence layer. The CRUD object calls generic methods (`read`, `create`, `update`, `delete`) on its data store, which handles the actual SQL.

### Built-In Data Stores

| Object | Data Store | Storage |
|--------|-----------|---------|
| Product | `WC_Product_Data_Store_CPT` | `wp_posts` + `wp_postmeta` |
| Order (legacy) | `WC_Order_Data_Store_CPT` | `wp_posts` + `wp_postmeta` |
| Order (HPOS) | `Automattic\WooCommerce\Internal\DataStores\Orders\OrdersTableDataStore` | `wp_wc_orders` + related tables |
| Customer | `WC_Customer_Data_Store` | `wp_users` + `wp_usermeta` |
| Coupon | `WC_Coupon_Data_Store_CPT` | `wp_posts` + `wp_postmeta` |

### Custom Data Stores

You can swap data stores via the `woocommerce_{object}_data_store` filter:
1. Implement `WC_Object_Data_Store_Interface` (or extend an existing data store)
2. Implement methods: `create()`, `read()`, `update()`, `delete()`, `read_meta()`, `update_meta()`, `delete_meta()`
3. Filter: `add_filter( 'woocommerce_product_data_store', function() { return 'My_Custom_Store'; } )`

## HPOS (High-Performance Order Storage)

### What Changed

HPOS moves order data from `wp_posts`/`wp_postmeta` to dedicated tables:
- **`wp_wc_orders`** — core order fields (status, currency, total, customer_id, dates)
- **`wp_wc_orders_meta`** — order meta
- **`wp_wc_order_addresses`** — billing/shipping addresses
- **`wp_wc_order_operational_data`** — internal operational data

### Why HPOS

- Massive performance improvement for stores with many orders
- Orders no longer pollute the `wp_posts` table
- Dedicated indexes for order queries
- Cleaner separation of concerns

### Compatibility Modes

- **Sync enabled** — both posts and custom tables are written (transition period)
- **Authoritative: Custom tables** — custom tables are the source of truth
- **Authoritative: Posts** — legacy mode, posts are source of truth

### Sync-on-read is off by default

**This is the failure mode to design against.** WooCommerce disabled HPOS *sync on read* by default in the 10.7 release cycle. Code that writes order data straight into `wp_postmeta` and expects it to appear in HPOS **now fails silently** — no error, no warning, just data that is never read back.

The practical consequence: legacy order writes are not merely discouraged any more, they are broken. Auditing a plugin for direct `wp_postmeta` order writes is no longer a tidiness exercise, it is a correctness fix.

Confirm the current default and the exact release in the live docs — fetch `https://developer.woocommerce.com/docs/features/orders/high-performance-order-storage/` — but write code as though sync is off regardless of version. It is the safe assumption in every direction.

### Declaring HPOS Compatibility

```php
add_action( 'before_woocommerce_init', function() {
    if ( class_exists( \Automattic\WooCommerce\Utilities\FeaturesUtil::class ) ) {
        \Automattic\WooCommerce\Utilities\FeaturesUtil::declare_compatibility(
            'custom_order_tables', __FILE__, true
        );
    }
});
```

### HPOS-Compatible Code

- **DO**: Use `$order->get_meta()`, `$order->update_meta_data()`, `$order->save()`
- **DO**: Use `wc_get_orders()` with proper args for querying
- **DON'T**: Use `get_post_meta()`, `update_post_meta()` on orders — with sync-on-read off, these writes are simply lost
- **DON'T**: Use `WP_Query` to query orders directly
- **DON'T**: Assume `$order->get_id()` is a `wp_posts` ID
- **DON'T**: Rely on a legacy write "still working because sync is on" — the default flipped, and it will not flip back

## Querying

### wc_get_orders()

The proper way to query orders (HPOS-compatible):
- Accepts args like `status`, `customer_id`, `date_created`, `meta_key`, `meta_value`
- Returns array of `WC_Order` objects
- Under the hood, delegates to the active order data store

### wc_get_products()

Query products — delegates to `WC_Product_Query`:
- `type`, `status`, `sku`, `category`, `tag`, `price`, `meta_key`, etc.
- Returns array of `WC_Product` objects

## Best Practices

- Always use CRUD getters/setters, never access `$data` directly
- Always call `save()` after modifying an object
- Use `'edit'` context for admin forms, `'view'` context for frontend display
- Use `wc_get_orders()` / `wc_get_products()` for queries, not `WP_Query`
- Declare HPOS compatibility in every extension
- Test with HPOS authoritative mode enabled

Fetch the WooCommerce CRUD and HPOS documentation for exact method signatures, query parameters, and compatibility requirements before implementing.

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…