WooCommerce order fields
6 min read
With Pro and WooCommerce active, a field group can appear on the order edit screen in the WooCommerce admin. Shop managers fill in extra information (a gift note, a delivery slot, an internal reference), and your templates and emails read it with wpmcf_get_order_field().
Plan | Pro, Business and Agency (feature |
Needs | WooCommerce active. Both order storages work: HPOS (custom order tables) and classic orders stored as posts. |
Where the boxes appear | The order edit screen only: WooCommerce › Orders › {order} (HPOS page |
Where values are saved | In the field group's own table, with |
Create the group
- WPM Fields › Field Groups › Add field group. Add your fields.
- In Location, choose a rule from the WooCommerce group (they exist only while Pro and WooCommerce are active):
Rule | Values | Matches |
|---|---|---|
Order | All orders ( | Every order. |
Order status | Every WooCommerce status ( | Orders that currently have this status. |
- Save the group, open an order.
A group is shown on orders only when it has an order rule. A group with a post type rule is not shown on orders, and a group with an order rule is not shown on posts. Add the rules in the same way as other rules (AND inside a rule set, OR between sets): Order status is equal to Processing OR Order status is equal to On hold.
"location": [
[ { "param": "wc_order_status", "operator": "equal", "value": "wc-processing" } ],
[ { "param": "wc_order_status", "operator": "equal", "value": "wc-on-hold" } ]
]You can define such a group in PHP too, see Field groups registered in PHP; use the same param and value as above.
How the status is evaluated
- The status is the order's current status when the screen loads. A group placed on Processing disappears when the order is Completed.
- When an admin changes the status and saves, WooCommerce applies the new status first; the plugin then evaluates the new status and saves the groups that match it. A group that matched only the old status does not save the values posted with that request.
- A new order that was not saved yet has the status auto-draft (or checkout-draft); it is treated as Pending payment, which is what it becomes on the first save.
On the order screen
- Each matching group gets a box, with the group's label as the title, its description and its fields. The group's Position setting (
normal,side,high) and Order decide where. Several groups are sorted by Order. - Styles and field behaviour are those of the post edit screen: conditional logic, validation messages, instructions, label placement.
- Saving: when the order is saved in the admin (Create / Update), every matching group is saved. The save checks a per-group nonce and the
edit_shop_orderscapability, so shop managers can save and other users cannot. - A field that is not part of the posted form keeps its value (verified).
- Password fields are rendered like on posts but
wpmcf_get_order_field()never returns them.
Values are stored only when the order is saved from the admin screen. Orders created by customers at checkout have no values until someone saves them in the admin; reading then returns your default.
Read the values: wpmcf_get_order_field()
wpmcf_get_order_field( string $group_key, string $field_key, int|WC_Order $order, mixed $default = null ): mixedArgument | Meaning |
|---|---|
| The keys from the builder. |
| An order ID or a |
| Returned when nothing is stored, the order, group or field is unknown, the field is a Password field, or the plugin is not ready. |
It returns the raw value, not escaped and not formatted: a Text field gives a string, True / False gives true / false, Checkbox gives an array, a Select gives the stored value, not the label. Escape it for where you print it. Reading does not need a license and does not check whether the group is active.
The function exists only in the Pro build. Guard the call if the same code may run on a site without Pro:
// Show the gift note in the "New order" and "Processing order" emails, below the order table.
add_action( 'woocommerce_email_after_order_table', function ( $order, $sent_to_admin, $plain_text ) {
if ( ! function_exists( 'wpmcf_get_order_field' ) ) {
return;
}
$note = wpmcf_get_order_field( 'order_extras', 'gift_note', $order );
if ( ! $note ) {
return;
}
if ( $plain_text ) {
echo "\n" . esc_html__( 'Gift note', 'my-theme' ) . ': ' . wp_strip_all_tags( $note ) . "\n";
} else {
echo '<p><strong>' . esc_html__( 'Gift note', 'my-theme' ) . ':</strong> ' . esc_html( $note ) . '</p>';
}
}, 20, 3 );In a template of your theme (for example woocommerce/order/order-details.php or the thank-you page):
<?php
$slot = function_exists( 'wpmcf_get_order_field' )
? wpmcf_get_order_field( 'order_extras', 'delivery_slot', $order, '' )
: '';
if ( $slot ) : ?>
<p><?php echo esc_html( $slot ); ?></p>
<?php endif; ?>For a field with choices, map the stored value to a label yourself, or read the label from the group definition.
What you cannot do (yet)
- No write function. Values are created from the order screen only.
wpmcf_update_field()andwpmcf_get_field()address posts, users, terms, comments and options; they do not address thewc_ordersrows. - Order fields are not in the WooCommerce REST API, WooCommerce exports, order search or order list columns.
- Shortcodes, blocks and builder dynamic tags read posts, not orders.
Data lifecycle and licenses
Situation | Boxes and saving | Values |
|
|---|---|---|---|
Plan includes the feature | Work | Stored | Works |
Trial or paid license ends (expired), or the site drops to Free | Off: no boxes, no saving | Kept | Still works (verified: a value stored before the lapse was still returned, and a save attempt after the lapse did not change it) |
License comes back | Work again | Same values | Works |
Order deleted permanently ( | The order's rows are deleted from every group table | Returns the default | |
Order moved to the trash | Kept (only a permanent delete removes rows) | Works | |
Group deleted in the editor | The table is removed with the group | Returns the default |
The location rules wc_order and wc_order_status stay registered after the license ends (verified on an expired and on a free dev plan), so groups with these rules still load and can be edited. The free WordPress.org build contains no Pro code, so it does not know these rules; what it does with such groups was not tested. The data rows stay in the tables either way.
Export to JSON (Tools) keeps working after expiry; values are not part of JSON exports.
Limitations and notes
- The boxes are added late on
add_meta_boxes(priority 20) with the screen ID and the order object; a plugin that removes all meta boxes on the order screen also removes these. - The plugin declares compatibility with High-Performance Order Storage (
custom_order_tables) when WooCommerce asks (before_woocommerce_init), so WooCommerce does not list it as incompatible. Verified with a stand-inFeaturesUtil(the call is made withtrue); a real HPOS site was not tested. - Rows are keyed by the order ID, whatever the storage. They stay valid as long as WooCommerce keeps the IDs of your orders.
Troubleshooting
Problem | Check |
|---|---|
The group does not appear on the order | The group is active and has an Order or Order status rule; the plan includes WooCommerce order fields and is not expired; WooCommerce is active; the group has been saved so its table exists. |
It appears for Processing but not for Completed orders | The location rule uses the status; add a second rule set. |
Nothing saves | You need |
| The Pro build is not active. Guard calls with |
The email shows nothing | The order was created by a customer and never saved in the admin, or the group/field key is wrong. The function returns your default. |
Still stuck? Contact support