Update meta 'attribute_' values when variation attributes are added or removed.

When a new variation attribute is added, the corresponding 'attribute_'
meta entries are added for all variations with an empty value;
and when an existing variation attribute is removed, the existing
'attribute_' meta entries are deleted for all variations.

This is necessary for the filter by attribute widget to work properly
when variations exist with a value of "Any..." for attributes.
This commit is contained in:
Nestor Soriano 2020-08-27 16:10:41 +02:00
parent a75da34a45
commit 1113201f8b
3 changed files with 67 additions and 1 deletions

View File

@ -468,10 +468,17 @@ class WC_Product_Variable extends WC_Product {
*/
do_action( 'woocommerce_before_' . $this->object_type . '_object_save', $this, $this->data_store );
// Get names before save.
// Get names and variation attributes before save.
$previous_name = $this->data['name'];
$new_name = $this->get_name( 'edit' );
if ( $this->get_id() ) {
$previous_product = wc_get_product( $this );
$previous_variation_attributes = array_keys( $this->data_store->read_variation_attributes( $previous_product ) );
} else {
$previous_variation_attributes = array();
}
if ( $this->get_id() ) {
$this->data_store->update( $this );
} else {
@ -481,6 +488,17 @@ class WC_Product_Variable extends WC_Product {
$this->data_store->sync_variation_names( $this, $previous_name, $new_name );
$this->data_store->sync_managed_variation_stock_status( $this );
// Get variation attributes after save.
$current_attributes = array_filter( array_values( $this->get_attributes() ) );
$current_variation_attributes = array();
foreach ( $current_attributes as $attribute ) {
if ( $attribute->get_variation() ) {
$current_variation_attributes[] = $attribute->get_name();
}
}
$this->data_store->sync_meta_for_variation_attributes( $this, $previous_variation_attributes, $current_variation_attributes );
/**
* Trigger action after saving to the DB.
*

View File

@ -631,6 +631,32 @@ class WC_Product_Variable_Data_Store_CPT extends WC_Product_Data_Store_CPT imple
}
}
/**
* Sync attribute metadata when variation attributes are added or removed.
*
* @param WC_Product $product Product object.
* @param array $previous_variation_attributes Names of the variation attributes the product had before saving.
* @param array $current_variation_attributes Names of the variation attributes the product has now.
*/
public function sync_meta_for_variation_attributes( $product, $previous_variation_attributes, $current_variation_attributes ) {
$added_attributes = array_diff( $current_variation_attributes, $previous_variation_attributes );
$removed_attributes = array_diff( $previous_variation_attributes, $current_variation_attributes );
$variation_ids = $product->get_children();
foreach ( $added_attributes as $attribute ) {
foreach ( $variation_ids as $variation_id ) {
update_post_meta( $variation_id, 'attribute_' . $attribute, '' );
}
}
foreach ( $removed_attributes as $attribute ) {
foreach ( $variation_ids as $variation_id ) {
delete_post_meta( $variation_id, 'attribute_' . $attribute );
}
}
}
/**
* Delete variations of a product.
*

View File

@ -39,4 +39,26 @@ class WC_Product_Variable_Test extends \WC_Unit_Test_Case {
$this->assertInstanceOf( WC_Product_Variation::class, $variations[0] );
$this->assertEquals( 'DUMMY SKU VARIABLE SMALL', $variations[0]->get_sku() );
}
/**
* @testdox 'save' adds empty 'attribute_' meta values for any newly added variation attribute, and removes the existing meta values for removed attributes.
*/
public function test_save_updates_meta_for_added_and_removed_variation_attributes() {
$product = WC_Helper_Product::create_variation_product();
$attributes = array_values( $product->get_attributes() );
$removed_attribute_name = $attributes[0]->get_name();
$attributes[0] = WC_Helper_Product::create_product_attribute_object( 'foobar', array( 'foo', 'bar' ) );
$product->set_attributes( $attributes );
$product->save();
$variation_ids = $product->get_children();
foreach ( $variation_ids as $variation_id ) {
// There's an empty attribute value for the added attribute...
$this->assertEquals( array( '' ), get_post_meta( $variation_id, 'attribute_pa_foobar' ) );
// ...and the attribute value for the removed attribute has been removed as well.
$this->assertEquals( array(), get_post_meta( $variation_id, 'attribute_' . $removed_attribute_name ) );
}
}
}