Free tools Windows power users keep installed
One-click scans. No signup required.
Use WooCommerce’s CRUD objects to change product, order, or customer metadata: load the object with wc_get_product(), wc_get_order(), or the appropriate CRUD loader; validate the value; call update_meta_data(); and finish with save(). The save is what sends the in-memory change through WooCommerce’s data store and persists it. Direct calls to WordPress metadata functions can bypass the CRUD layer and should be reserved for cases where you have a specific compatibility reason.
Antihost offers managed WordPress hosting with free migration, one-click staging and a 14-day free trial.
What update_meta_data() does
update_meta_data() is the metadata setter on WooCommerce CRUD data objects. Products, orders, customers, and other objects expose the method so extensions can change custom fields without manipulating the underlying WordPress post object directly.
As an Amazon Associate I earn from qualifying purchases.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
WooCommerce introduced its CRUD object system in version 3.0. Its developer guidance is to use those objects whenever possible rather than directly updating metadata or WordPress post objects. A CRUD object holds the change in memory, and WC_Data::save() asks the configured data store to create or update the record and its metadata. If you never save the object, the mutation is discarded when the request ends.
The canonical PHP pattern
The essential sequence is always the same: load, check, validate, update, save.
#1 Best Overall
Updating product metadata
<?php
$product = wc_get_product( $product_id );
if ( ! $product ) {
return;
}
$value = sanitize_text_field( wp_unslash( $_POST['external_ref'] ?? '' ) );
$product->update_meta_data( '_external_ref', $value );
$product->save();
wc_get_product() returns the product CRUD object for the supplied ID or a falsey value when that ID is not a valid product. The guard prevents a method call on a missing object. The example removes slashes added to request data, sanitizes a text value, updates a plugin-specific key, and persists the object.
Updating order metadata
<?php
$order = wc_get_order( $order_id );
if ( ! $order ) {
return;
}
$order->update_meta_data( '_fulfillment_note', $note );
$order->save();
The same lifecycle applies to an order. Validate $note according to the field’s intended type before the setter. If it came from an administrator form, perform the capability and nonce checks before accepting it; sanitizing a value does not authorize the user to change the order.
The Tool Desk
Outbyte Driver Updater FREEFix the driver behind crashes, sound loss and screen glitchesFind Drivers →Outbyte PC Repair FREERepair Windows errors before they cause bigger problemsFix Now →Method arguments and update semantics
The method accepts a metadata key, a scalar or array value, and optionally a metadata ID. In conceptual form, the call is:
$object->update_meta_data( $key, $value, $meta_id = 0 );
Key and value
The key identifies the metadata field. The value can be a scalar such as a string, integer, or boolean, or an array when the consuming code expects structured data. Choose a representation deliberately and use the same representation when reading it back. A value that is technically accepted by PHP can still be wrong for code that expects a string, number, or array.
Use a stable, extension-specific key such as _acme_external_ref. A prefix reduces collisions with WooCommerce core and other extensions. Keep the leading underscore consistent: _external_ref and external_ref are different keys.
Key-based updates
When no metadata ID is supplied, WooCommerce applies its internal key-based metadata handling to the matching key. This is the right choice when the key represents one current value and your code does not need to distinguish between duplicate rows.
Outdated Drivers Are Slowing You Down
One free scan finds every outdated or missing driver and matches the right update for your exact hardware.Free scan · exact hardware matchPC Slower Than It Used to Be?
A free scan shows the junk files, broken settings and background clutter dragging Windows down - then fixes them in one click.Free scan · Windows 10 & 11Do not treat a generic update call as an append operation. If your design requires several values under one key, define how those values are represented and how readers select them, then test that behavior with the active data store. A single-value design is usually easier for other extensions to read and maintain.
Updating one duplicate row by metadata ID
Metadata can contain more than one row with the same key, for example after a migration or a legacy integration. If one particular row must change while the others remain untouched, pass that row’s real metadata ID as the third argument.
<?php
$product = wc_get_product( $product_id );
if ( ! $product ) {
return;
}
$meta_id = 123; // Obtain the real metadata row ID for this product.
$product->update_meta_data( '_legacy_code', 'NEW-VALUE', $meta_id );
$product->save();
The number in the example is illustrative; use the ID belonging to the row you intend to change. Supplying an ID makes the target unambiguous. Without it, the key-based path may update a matching entry according to WooCommerce’s internal handling rather than the specific duplicate you had in mind.
Why save() is mandatory
CRUD setters modify the object currently held in PHP. They do not, by themselves, guarantee a database write. save() delegates create-or-update work to the configured data store, including metadata persistence. WooCommerce’s rule is that saving should create or update based on whether the object already exists.
Call save() after all related mutations have been made, rather than saving after every individual field. This keeps one logical change together and avoids unnecessary writes.
Rank #2
<?php
$order = wc_get_order( $order_id );
if ( ! $order ) {
return;
}
$order->update_meta_data( '_fulfillment_note', $note );
$order->update_meta_data( '_carrier_reference', $carrier_reference );
$order->save();
If a later hook changes the same key before the save occurs, the later in-memory value is the one that is persisted. When debugging, inspect the value immediately before saving and then reload the object after saving.
CRUD objects versus direct WordPress metadata functions
update_post_meta() can appear simpler, especially for products and orders that are represented by WordPress posts. It is not the preferred default for WooCommerce data, however. CRUD objects provide structured getters and setters, centralize persistence through a data store, and are designed to remain compatible with alternate storage implementations.
| Decision point | WooCommerce CRUD object | Direct WordPress metadata call |
|---|---|---|
| Typical call | $product->update_meta_data( $key, $value );, followed by save() |
update_post_meta( $post_id, $key, $value ); |
| Persistence model | Changes are held by the object and written by its configured data store when saved | Writes directly through WordPress metadata APIs |
| Compatibility with alternate data stores | Best fit because the data-store layer owns storage details | May bypass storage behavior expected by a custom WooCommerce data store |
| Validation and structure | Works alongside WooCommerce’s object properties and lifecycle | Leaves type, authorization, and WooCommerce lifecycle decisions to your code |
| Use when | You are changing a product, order, customer, or other WooCommerce CRUD object | You have a deliberate legacy or integration constraint and have verified its storage implications |
The comparison is not a claim that WordPress metadata functions never work. It is a compatibility decision: if the value belongs to a WooCommerce object, load and save that object unless a documented requirement says otherwise.
Metadata is not a substitute for core WooCommerce properties
Custom metadata is appropriate for extension-owned information such as an external reference, fulfillment note, or legacy code. It is the wrong place to shadow a property WooCommerce already models.
- Use the dedicated CRUD setter for a product price instead of storing a second custom price field.
- Use the supported stock methods for inventory instead of maintaining a competing stock number in metadata.
- Use the order status API for status changes instead of a metadata flag that merely resembles a status.
Dedicated properties provide WooCommerce’s structure and validation and are more discoverable to other extensions. A custom key should carry information that is genuinely owned by your integration.
Validation, naming, and security
Validate for the field’s actual type
Sanitization is only one part of validation. A plain text reference can use text sanitization; a number should be checked as a number and constrained to the range your business rule permits; a structured array should be validated item by item. Reject malformed values rather than silently converting them into a different meaning.
<?php
$raw_reference = wp_unslash( $_POST['external_ref'] ?? '' );
$external_ref = sanitize_text_field( $raw_reference );
if ( '' === $external_ref ) {
return;
}
$product->update_meta_data( '_acme_external_ref', $external_ref );
$product->save();
The example treats an empty reference as invalid. Your field may legitimately allow an empty value; in that case, define whether empty means “clear the metadata” or “leave the previous value unchanged” and implement that distinction explicitly.
Authorize administrator-originated writes
For values submitted from an administrator form, verify the user’s capability and the form nonce before processing the request. Then unslash and validate the value. The order matters: a valid nonce does not replace a capability check, and a sanitized string does not prove that the requester is allowed to edit the object.
REST API metadata updates
WooCommerce REST updates carry metadata as JSON in a meta_data array. A minimal payload is:
{
"meta_data": [
{"key": "_external_ref", "value": "ABC-123"}
]
}
WooCommerce normalizes REST metadata entries before applying them to the target WC_Data object. Its metadata utility skips entries without a key, applies defaults for missing fields, and calls update_meta_data() for valid entries. The object is then saved as part of the REST update lifecycle.
Use an authenticated WooCommerce REST endpoint for writes. The Store API is public and intended for customer-facing cart, checkout, and product functionality; it is not a general-purpose administrative metadata write interface.
REST-specific checks
- Send an array of objects, not a single metadata object.
- Include a non-empty
key; entries without one are skipped. - Match the value type expected by the extension that reads the field.
- Authenticate the request with credentials appropriate for the WooCommerce REST API.
- After the response, reload the object or issue a read request to verify the persisted value.
Single-value and multi-value designs
When one key should have one current value
Use one stable key and update it whenever the current value changes. This is the clearest model for an external reference, a fulfillment note, or a synchronization timestamp. Readers can request the key without having to decide which duplicate row is authoritative.
When several values are intentional
If the business model genuinely has multiple values, choose one of two explicit designs: store a validated array under one key, or create separate, well-defined keys for distinct concepts. Document how consuming code reads and updates the values. Do not rely on accidental duplicate rows created by repeated generic updates, because the data-store layer owns the details of metadata persistence and key matching.
Working with the data-store layer
WooCommerce data stores bridge classes such as WC_Product, WC_Order, and WC_Customer to database storage. They define how metadata is read, added, updated, and deleted. This separation is why the CRUD route is safer when an installation uses a custom or alternate data store.
Your extension should therefore avoid assuming that metadata is always stored as a particular WordPress post-meta row or that a direct database operation has the same lifecycle as save(). Let the object and its data store perform persistence unless you are implementing a storage integration yourself.
Recommended Free Tools
A reliable implementation workflow
- Identify the owner. Decide whether the value belongs to a product, order, customer, or another WooCommerce object. If it is a core property such as price or stock, use that property’s CRUD setter instead of metadata.
- Load through the CRUD class. Use
wc_get_product(),wc_get_order(), or the corresponding WooCommerce loader. Stop cleanly if no object is returned. - Authorize the operation. For administrator forms, check the required capability and verify the nonce before accepting input. For REST, use an authenticated WooCommerce endpoint.
- Normalize and validate. Unslash request data, sanitize it for its type, and apply business constraints such as required fields, ranges, or allowed values.
- Choose the update target. Use the key alone for a normal current-value field. Supply the metadata ID when duplicate rows exist and one specific row must change.
- Apply all related mutations. Call
update_meta_data()for each custom field that belongs to the same logical change. - Persist once. Call
save()after the mutations. Without this call, the in-memory changes are not guaranteed to reach storage. - Verify. Reload the object and read the metadata again. A successful method call only proves that PHP accepted the mutation; the reload verifies persistence.
Debugging “update_meta_data did not save”
Work through the checks in this order so that a missing object or key mismatch is not mistaken for a database failure.
| Symptom | Likely cause | What to check |
|---|---|---|
| No value changes at all | The loader returned no object | Inspect the result of wc_get_product() or wc_get_order() before calling the setter. |
| The setter runs but the old value remains | save() was omitted |
Call $object->save() after the final metadata mutation. |
| A read appears empty | The read key differs from the write key | Compare spelling, capitalization, and leading underscores exactly. |
| The value changes and then reverts | A later hook or callback overwrites it before save or on a subsequent save | Log or inspect the value immediately before saving and identify later callbacks touching the same key. |
| The wrong duplicate row changes | The update used only a key where a row-specific update was required | Obtain the intended metadata row ID and pass it as the third argument. |
| Behavior differs between installations | A custom data store or hook changes persistence behavior | Review the active data-store implementation and callbacks rather than assuming direct post-meta behavior. |
| REST input is ignored | The metadata object has no key or the request is using the wrong API surface | Send a keyed entry through an authenticated WooCommerce REST endpoint; do not use the public Store API for administrative writes. |
Reload to prove persistence
<?php
$product->update_meta_data( '_external_ref', $external_ref );
$product->save();
$reloaded_product = wc_get_product( $product->get_id() );
$stored_reference = $reloaded_product
? $reloaded_product->get_meta( '_external_ref', true )
: '';
Reloading separates an in-memory value from a value that survived the data-store write. If the reloaded value is wrong, inspect the key, the saved object ID, hooks, and any custom data-store behavior.
Patterns for maintainable extension code
Keep metadata ownership clear
Choose keys that identify the extension and document the type and meaning of each field. A future developer should be able to tell whether a key is a scalar reference, a serialized structure represented by an array, or a flag that should be replaced by a core property.
Group related changes
Load the object once, validate all incoming fields, update the object, and save once. This reduces partial updates in code paths that change several values together. If validation fails, return before mutating the object.
Do these 3 things before closing this tab:
1Repair Windows errors before they cause bigger problems2Scan for outdated or missing drivers - takes under a minute3Clear out junk files and repair common Windows errorsDo not infer append behavior
If history is required, model history deliberately instead of expecting repeated calls with the same key to create an audit trail. Metadata updates are for maintaining the object’s current extension state; an audit log has different retention and retrieval requirements.
Choosing an approach at a glance
| Your requirement | Recommended approach | Reason |
|---|---|---|
| Change one current custom value on a product | Load the product, call update_meta_data( $key, $value ), then save() |
Uses the WooCommerce CRUD lifecycle and lets the data store persist the change. |
| Change one current custom value on an order | Load the order, validate the value, update metadata, then save | Keeps order-owned extension data on the order object. |
| Change one of several rows sharing a key | Pass the specific metadata ID as the third argument, then save | Identifies the exact duplicate row. |
| Maintain several values intentionally | Define an array or separate keys and test the reader’s behavior | A generic update does not define an append contract. |
| Change price, stock, or status | Use the dedicated CRUD property setter | Core properties have WooCommerce structure and validation. |
| Write from an external integration | Use authenticated WooCommerce REST JSON with a meta_data array |
The REST layer normalizes entries and applies them to the CRUD object. |
| Operate where an alternate data store may exist | Prefer the CRUD object and save() |
The data-store abstraction owns storage details. |
Complete example with validation and verification
<?php
$product = wc_get_product( $product_id );
if ( ! $product ) {
return;
}
// Capability and nonce checks belong before this point for form submissions.
$raw_reference = wp_unslash( $_POST['external_ref'] ?? '' );
$external_ref = sanitize_text_field( $raw_reference );
if ( '' === $external_ref ) {
return;
}
$product->update_meta_data( '_acme_external_ref', $external_ref );
$product->save();
$reloaded_product = wc_get_product( $product->get_id() );
if ( ! $reloaded_product ) {
return;
}
$stored_reference = $reloaded_product->get_meta( '_acme_external_ref', true );
This sequence keeps the responsibilities distinct: object loading, authorization, input handling, metadata mutation, persistence, and verification. Adapt the validation rule and key to the field you own; do not copy a text sanitizer for a value whose type is numeric or structured.
Bottom line
For WooCommerce metadata, the dependable default is load → validate → update_meta_data() → save → reload and verify. Use a plugin-specific key for a single current value, pass a metadata ID when duplicate rows make the target ambiguous, model multi-value data explicitly, and keep prices, stock, and status in their dedicated CRUD properties. The object and its data store are the compatibility boundary; bypassing them should be a conscious exception, not the starting point.
Quick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.




