Fix bug #15103 where site operator cannot assign a variation with value of '0' as the default choice on the customer facing form.

The WC_Product::set_default_attributes function uses an array_filter (using the default callback for filtration)
to remove null and false values from the defaults array for a given product.  The issue here is that, in the above use case,
the array_filter will evaluate '0' as 0 and therefore as false.  Ultimately, array_filter then prevents the value from being
recorded, moving forward.

I've added a new filter callback to includes/wc-attribute-functions which will disregard all FALSE PHP equivalents except for
'0' (as a a string).  Also, I've updated the filter_array call in WC_Product::set_default_attributes so that it uses this new callback,
instead of the PHP default. Finally, I've added a phpunit test to assert that, when storing default variations / attributes on a product,
the false/true PHP synonyms are evaluating and storing like one would normally expect, with the exception that (string) '0'
evaluates as true in this special case.

This solution could potentially be broadened to facililate similar rules elsewhere, but the need raised in the bug is specific and
this is a specific solution.
This commit is contained in:
Timon Davis 2017-05-18 02:43:14 -07:00
parent f5bda12e42
commit af308da6b9
3 changed files with 53 additions and 2 deletions

View File

@ -1066,7 +1066,8 @@ class WC_Product extends WC_Abstract_Legacy_Product {
* @param array $default_attributes List of default attributes.
*/
public function set_default_attributes( $default_attributes ) {
$this->set_prop( 'default_attributes', array_filter( (array) $default_attributes ) );
$this->set_prop( 'default_attributes',
array_filter( (array) $default_attributes, 'wc_array_filter_default_attributes' ) );
}
/**

View File

@ -332,3 +332,15 @@ function wc_is_attribute_in_product_name( $attribute, $name ) {
$is_in_name = stristr( $name, ' ' . $attribute . ',' ) || 0 === stripos( strrev( $name ), strrev( ' ' . $attribute ) );
return apply_filters( 'woocommerce_is_attribute_in_product_name', $is_in_name, $attribute, $name );
}
/**
* Callback for array filter to get default attributes. Will allow for '0' string values, but regard all other
* class PHP FALSE equivalents normally.
*
* @since 3.1.0
* @param mixed $attribute Attribute being considered for exclusion from parent array.
* @return bool
*/
function wc_array_filter_default_attributes( $attribute ) {
return ( ! empty( $attribute ) || $attribute === '0' );
}

View File

@ -316,7 +316,7 @@ class WC_Tests_Product_Data_Store extends WC_Unit_Test_Case {
$product->delete();
}
function test_varation_save_attributes() {
function test_variation_save_attributes() {
// Create a variable product with a color attribute.
$product = new WC_Product_Variable;
@ -350,6 +350,44 @@ class WC_Tests_Product_Data_Store extends WC_Unit_Test_Case {
$this->assertEquals( 'green', $_attribute['color'] );
}
public function test_save_default_attributes() {
// Create a variable product with sold individually.
$product = new WC_Product_Variable;
$product->save();
$product_id = $product->get_id();
// Save with a set of FALSE equivalents and some values we expect to come through as true. We should see
// string types with a value of '0' making it through filtration.
$test_object = new stdClass();
$test_object->property = '12345';
$product->set_default_attributes( array(
'sample-attribute-false-0' => 0,
'sample-attribute-false-1' => false,
'sample-attribute-false-2' => '',
'sample-attribute-false-3' => null,
'sample-attribute-true-0' => '0',
'sample-attribute-true-1' => 1,
'sample-attribute-true-2' => 'true',
'sample-attribute-true-3' => 'false',
'sample-attribute-true-4' => array( 'exists' => 'false' ),
'sample-attribute-false-4' => $test_object,
));
$product->save();
// Revive the product from the database and analyze results
$product = wc_get_product( $product_id );
$default_attributes = $product->get_default_attributes();
$this->assertEquals( $default_attributes, array(
'sample-attribute-true-0' => '0',
'sample-attribute-true-1' => 1,
'sample-attribute-true-2' => 'true',
'sample-attribute-true-3' => 'false',
'sample-attribute-true-4' => array( 'exists' => 'false' ),
'sample-attribute-false-4' => $test_object,
));
}
function test_variable_child_has_dimensions() {
$product = new WC_Product_Variable;
$product->save();