Commit 580b090e365 for woocommerce
commit 580b090e36514ffc4ee91e63613e6da62b3bfdfd
Author: Puneet Dixit <puneetdixit4321@gmail.com>
Date: Sat Jul 11 08:59:55 2026 +0530
Fix order meta_data schema to use a standard type union instead of 'mixed' (#65513)
Replaces the non-standard JSON Schema 'mixed' type on every order meta_data 'value' field (and line item 'display_value') with the explicit union array( 'null', 'object', 'string', 'number', 'boolean', 'integer', 'array' ), and marks the line item display fields read-only. This lets REST order updates accept round-tripped line item meta display values with complex data, and makes the published orders schema standards-compliant. Behavior-preserving: the write/validation path already normalized 'mixed' to this exact union.
Closes #32038.
diff --git a/plugins/woocommerce/changelog/fix-32038-line-item-meta-display-values b/plugins/woocommerce/changelog/fix-32038-line-item-meta-display-values
new file mode 100644
index 00000000000..5ab9012f4f7
--- /dev/null
+++ b/plugins/woocommerce/changelog/fix-32038-line-item-meta-display-values
@@ -0,0 +1,4 @@
+Significance: patch
+Type: fix
+
+Allow REST order updates to accept round-tripped line item meta display values with complex data.
diff --git a/plugins/woocommerce/includes/rest-api/Controllers/Version2/class-wc-rest-orders-v2-controller.php b/plugins/woocommerce/includes/rest-api/Controllers/Version2/class-wc-rest-orders-v2-controller.php
index 80385dbf660..fa8bde2a2a3 100644
--- a/plugins/woocommerce/includes/rest-api/Controllers/Version2/class-wc-rest-orders-v2-controller.php
+++ b/plugins/woocommerce/includes/rest-api/Controllers/Version2/class-wc-rest-orders-v2-controller.php
@@ -1480,7 +1480,7 @@ class WC_REST_Orders_V2_Controller extends WC_REST_CRUD_Controller {
),
'value' => array(
'description' => __( 'Meta value.', 'woocommerce' ),
- 'type' => 'mixed',
+ 'type' => array( 'null', 'object', 'string', 'number', 'boolean', 'integer', 'array' ),
'context' => array( 'view', 'edit' ),
),
),
@@ -1597,18 +1597,20 @@ class WC_REST_Orders_V2_Controller extends WC_REST_CRUD_Controller {
),
'value' => array(
'description' => __( 'Meta value.', 'woocommerce' ),
- 'type' => 'mixed',
+ 'type' => array( 'null', 'object', 'string', 'number', 'boolean', 'integer', 'array' ),
'context' => array( 'view', 'edit' ),
),
'display_key' => array(
'description' => __( 'Meta key for UI display.', 'woocommerce' ),
'type' => 'string',
'context' => array( 'view', 'edit' ),
+ 'readonly' => true,
),
'display_value' => array(
- 'description' => __( 'Meta value for UI display.', 'woocommerce' ),
- 'type' => 'string',
+ 'description' => __( 'Meta value for UI display. May be a string or, when mirroring a complex meta value, an array or object.', 'woocommerce' ),
+ 'type' => array( 'null', 'object', 'string', 'number', 'boolean', 'integer', 'array' ),
'context' => array( 'view', 'edit' ),
+ 'readonly' => true,
),
),
),
@@ -1723,7 +1725,7 @@ class WC_REST_Orders_V2_Controller extends WC_REST_CRUD_Controller {
),
'value' => array(
'description' => __( 'Meta value.', 'woocommerce' ),
- 'type' => 'mixed',
+ 'type' => array( 'null', 'object', 'string', 'number', 'boolean', 'integer', 'array' ),
'context' => array( 'view', 'edit' ),
),
),
@@ -1814,7 +1816,7 @@ class WC_REST_Orders_V2_Controller extends WC_REST_CRUD_Controller {
),
'value' => array(
'description' => __( 'Meta value.', 'woocommerce' ),
- 'type' => 'mixed',
+ 'type' => array( 'null', 'object', 'string', 'number', 'boolean', 'integer', 'array' ),
'context' => array( 'view', 'edit' ),
),
),
@@ -1912,7 +1914,7 @@ class WC_REST_Orders_V2_Controller extends WC_REST_CRUD_Controller {
),
'value' => array(
'description' => __( 'Meta value.', 'woocommerce' ),
- 'type' => 'mixed',
+ 'type' => array( 'null', 'object', 'string', 'number', 'boolean', 'integer', 'array' ),
'context' => array( 'view', 'edit' ),
),
),
@@ -1988,7 +1990,7 @@ class WC_REST_Orders_V2_Controller extends WC_REST_CRUD_Controller {
),
'value' => array(
'description' => __( 'Meta value.', 'woocommerce' ),
- 'type' => 'mixed',
+ 'type' => array( 'null', 'object', 'string', 'number', 'boolean', 'integer', 'array' ),
'context' => array( 'view', 'edit' ),
),
),
diff --git a/plugins/woocommerce/tests/php/includes/rest-api/Controllers/Version3/class-wc-rest-orders-controller-tests.php b/plugins/woocommerce/tests/php/includes/rest-api/Controllers/Version3/class-wc-rest-orders-controller-tests.php
index a0958cc8f95..ccb31880ea0 100644
--- a/plugins/woocommerce/tests/php/includes/rest-api/Controllers/Version3/class-wc-rest-orders-controller-tests.php
+++ b/plugins/woocommerce/tests/php/includes/rest-api/Controllers/Version3/class-wc-rest-orders-controller-tests.php
@@ -1023,4 +1023,98 @@ class WC_REST_Orders_Controller_Tests extends WC_REST_Unit_Test_Case {
$this->assert_incomplete_meta_data_handled_correctly( wc_get_order( $order->get_id() ) );
}
+
+ /**
+ * @testdox Updating an order accepts round-tripped line item meta_data display values with complex values.
+ */
+ public function test_update_line_item_meta_data_ignores_display_values_with_complex_values(): void {
+ $product = WC_Helper_Product::create_simple_product();
+ $complex_meta_value = array(
+ array(
+ 'guid' => 'https://example.com/wp-content/uploads/2022/03/upload.jpg',
+ 'file_type' => 'image/jpeg',
+ 'file_name' => 'upload.jpg',
+ 'title' => 'upload',
+ 'key' => 'file-key',
+ ),
+ );
+
+ $request = new WP_REST_Request( 'POST', '/wc/v3/orders' );
+ $request->set_body_params(
+ array(
+ 'line_items' => array(
+ array(
+ 'product_id' => $product->get_id(),
+ 'quantity' => 1,
+ 'meta_data' => array(
+ array(
+ 'key' => '_file_upload_data',
+ 'value' => $complex_meta_value,
+ ),
+ ),
+ ),
+ ),
+ )
+ );
+
+ $response = $this->server->dispatch( $request );
+ $this->assertEquals( 201, $response->get_status() );
+
+ $response_data = $response->get_data();
+ $line_item_data = $response_data['line_items'][0];
+ $meta_data = $line_item_data['meta_data'][0];
+ $this->assertIsArray( $meta_data['display_value'] );
+
+ $request = new WP_REST_Request( 'PUT', '/wc/v3/orders/' . $response_data['id'] );
+ $request->set_header( 'content-type', 'application/json' );
+ $request->set_body(
+ wp_json_encode(
+ array(
+ 'status' => OrderStatus::PROCESSING,
+ 'line_items' => array(
+ array(
+ 'id' => $line_item_data['id'],
+ 'meta_data' => array( $meta_data ),
+ ),
+ ),
+ )
+ )
+ );
+
+ $response = $this->server->dispatch( $request );
+ $this->assertEquals( 200, $response->get_status() );
+
+ $order = wc_get_order( $response_data['id'] );
+ $line_items = $order->get_items( 'line_item' );
+ $line_item = $line_items[ $line_item_data['id'] ];
+ $this->assertEquals( $complex_meta_value, $line_item->get_meta( '_file_upload_data' ) );
+ }
+
+ /**
+ * @testdox Published order schema advertises a standard type union for meta_data value/display_value, not the non-standard 'mixed'.
+ */
+ public function test_meta_data_schema_advertises_type_union(): void {
+ $expected_union = array( 'null', 'object', 'string', 'number', 'boolean', 'integer', 'array' );
+
+ $schema = $this->endpoint->get_item_schema();
+ $properties = $schema['properties'];
+
+ // Every order meta_data value field, at every level, should advertise the union instead of 'mixed'.
+ $meta_containers = array(
+ $properties['meta_data']['items']['properties'],
+ $properties['line_items']['items']['properties']['meta_data']['items']['properties'],
+ $properties['shipping_lines']['items']['properties']['meta_data']['items']['properties'],
+ $properties['fee_lines']['items']['properties']['meta_data']['items']['properties'],
+ $properties['coupon_lines']['items']['properties']['meta_data']['items']['properties'],
+ );
+ foreach ( $meta_containers as $meta_properties ) {
+ $this->assertEquals( $expected_union, $meta_properties['value']['type'] );
+ }
+
+ // The line item block also exposes read-only display fields with the same union type.
+ $line_item_meta = $properties['line_items']['items']['properties']['meta_data']['items']['properties'];
+ $this->assertEquals( $expected_union, $line_item_meta['display_value']['type'] );
+ $this->assertTrue( $line_item_meta['display_value']['readonly'] );
+ $this->assertTrue( $line_item_meta['display_key']['readonly'] );
+ }
}