Commit 90768f288f for woocommerce
commit 90768f288f97c903e414196f7a05128f3fbc6991
Author: Albert Juhé Lluveras <contact@albertjuhe.com>
Date: Tue Dec 9 10:35:30 2025 +0100
Fix REST API handling of attribute names containing special characters when creating product variations (#61939)
* Allow creating variations with attribute names containing special characters using the Rest API
* Add changelog file
* Improve test comments
* Decode attribute slug before returning it
diff --git a/plugins/woocommerce/changelog/fix-create-variation-special-chras-rest-api b/plugins/woocommerce/changelog/fix-create-variation-special-chras-rest-api
new file mode 100644
index 0000000000..4b37ee64e5
--- /dev/null
+++ b/plugins/woocommerce/changelog/fix-create-variation-special-chras-rest-api
@@ -0,0 +1,4 @@
+Significance: patch
+Type: fix
+
+Fixed REST API handling of attribute names containing special characters when creating product variations
diff --git a/plugins/woocommerce/includes/rest-api/Controllers/Version2/class-wc-rest-products-v2-controller.php b/plugins/woocommerce/includes/rest-api/Controllers/Version2/class-wc-rest-products-v2-controller.php
index e76a89158a..669a28484f 100644
--- a/plugins/woocommerce/includes/rest-api/Controllers/Version2/class-wc-rest-products-v2-controller.php
+++ b/plugins/woocommerce/includes/rest-api/Controllers/Version2/class-wc-rest-products-v2-controller.php
@@ -614,7 +614,7 @@ class WC_REST_Products_V2_Controller extends WC_REST_CRUD_Controller {
$attributes[] = array(
'id' => wc_attribute_taxonomy_id_by_name( $name ),
'name' => $this->get_attribute_taxonomy_name( $name, $_product ),
- 'slug' => $name,
+ 'slug' => rawurldecode( $name ),
'option' => $option_term && ! is_wp_error( $option_term ) ? $option_term->name : $attribute,
);
} else {
diff --git a/plugins/woocommerce/includes/rest-api/Controllers/Version3/class-wc-rest-product-variations-controller.php b/plugins/woocommerce/includes/rest-api/Controllers/Version3/class-wc-rest-product-variations-controller.php
index c464eeaf8f..fc3d505938 100644
--- a/plugins/woocommerce/includes/rest-api/Controllers/Version3/class-wc-rest-product-variations-controller.php
+++ b/plugins/woocommerce/includes/rest-api/Controllers/Version3/class-wc-rest-product-variations-controller.php
@@ -350,7 +350,7 @@ class WC_REST_Product_Variations_Controller extends WC_REST_Product_Variations_V
// Check ID for global attributes or name for product attributes.
if ( ! empty( $attribute['id'] ) ) {
$attribute_id = absint( $attribute['id'] );
- $attribute_name = wc_attribute_taxonomy_name_by_id( $attribute_id );
+ $attribute_name = sanitize_title( wc_attribute_taxonomy_name_by_id( $attribute_id ) );
} elseif ( ! empty( $attribute['name'] ) ) {
$attribute_name = sanitize_title( $attribute['name'] );
}
diff --git a/plugins/woocommerce/tests/php/includes/rest-api/Controllers/Version3/class-wc-rest-product-variations-controller-tests.php b/plugins/woocommerce/tests/php/includes/rest-api/Controllers/Version3/class-wc-rest-product-variations-controller-tests.php
index 6a954077fa..a26374466e 100644
--- a/plugins/woocommerce/tests/php/includes/rest-api/Controllers/Version3/class-wc-rest-product-variations-controller-tests.php
+++ b/plugins/woocommerce/tests/php/includes/rest-api/Controllers/Version3/class-wc-rest-product-variations-controller-tests.php
@@ -166,6 +166,66 @@ class WC_REST_Product_Variations_Controller_Tests extends WC_REST_Unit_Test_Case
$this->assertEquals( 200, $response->get_status() );
}
+ /**
+ * Test that creating a variation with attributes containing special characters in their slug
+ * properly saves the attributes.
+ *
+ * This test verifies the fix for issue #61791 where attributes with non-ASCII characters
+ * (like Persian) were not saved when creating variations via the REST API.
+ * @see https://github.com/woocommerce/woocommerce/issues/61791
+ */
+ public function test_create_variation_with_persian_attribute_by_id() {
+ // Create a variable product with Persian attribute names.
+ $product = WC_Helper_Product::create_variation_product();
+ $color_attribute = WC_Helper_Product::create_product_attribute_object( 'رنگ', array( 'blue', 'green' ) );
+ $product->set_attributes( array( $color_attribute ) );
+ $product->save();
+
+ // Create a variation via REST API.
+ $request = new WP_REST_Request( 'POST', '/wc/v3/products/' . $product->get_id() . '/variations' );
+ $request->set_body_params(
+ array(
+ 'regular_price' => '0',
+ 'sale_price' => '0',
+ 'manage_stock' => true,
+ 'stock_quantity' => 0,
+ 'attributes' => array(
+ array(
+ 'id' => $color_attribute->get_id(),
+ 'option' => 'green',
+ ),
+ ),
+ )
+ );
+
+ $response = $this->server->dispatch( $request );
+ $variation = $response->get_data();
+
+ // Verify the variation was created successfully.
+ $this->assertEquals( 201, $response->get_status() );
+ $this->assertNotEmpty( $variation['id'] );
+ $this->assertEquals( 'variation', $variation['type'] );
+ $this->assertEquals( $product->get_id(), $variation['parent_id'] );
+
+ // Verify the attribute is properly set.
+ $this->assertNotEmpty( $variation['attributes'], 'Attributes array should not be empty' );
+ $this->assertCount( 1, $variation['attributes'], 'Variation should have 1 attribute' );
+ $this->assertEquals( 'رنگ', $variation['attributes'][0]['name'], 'Variation should contain color attribute' );
+ $this->assertEquals( 'green', $variation['attributes'][0]['option'], 'Variation should contain color option green' );
+
+ // Verify the variation can be retrieved and has the attribute data.
+ $get_request = new WP_REST_Request( 'GET', '/wc/v3/products/' . $product->get_id() . '/variations/' . $variation['id'] );
+ $get_response = $this->server->dispatch( $get_request );
+ $retrieved_variation = $get_response->get_data();
+
+ $this->assertEquals( 200, $get_response->get_status() );
+ $this->assertNotEmpty( $retrieved_variation['attributes'], 'Retrieved variation should have attributes' );
+ $this->assertCount( 1, $retrieved_variation['attributes'], 'Retrieved variation should have 1 attribute' );
+ $this->assertEquals( 'رنگ', $retrieved_variation['attributes'][0]['name'], 'Variation should contain color attribute' );
+ $this->assertEquals( 'pa_رنگ', $retrieved_variation['attributes'][0]['slug'], 'Variation should contain color attribute slug' );
+ $this->assertEquals( 'green', $retrieved_variation['attributes'][0]['option'], 'Variation should contain color option green' );
+ }
+
/**
* Test that the products endpoint can filter by global_unique_id and also return matched variations.
*