7

Objective: add product to cart despite unselected variations, i.e. remove/disable the mandatory nature of variation fields.

Issue: WooCommerce's absolute requirement for all variations to be selected before adding to cart.

Tried: filtering out/removing/disabling unselected variations before adding to cart using various hooks; woocommerce_before_calculate_totals, woocommerce_add_to_cart, woocommerce_add_cart_item, woocommerce_add_to_cart_validation

I understand that this is how WooCommerce works and the reasons why it works this way - despite this I still require a work-around.

How do I get around WooCommerce's "all variations selected" requirement so that I may still add a product to the cart even if not all variations are selected?

UncaughtTypeError
  • 6,718
  • 2
  • 17
  • 35
  • If you ask me the quickest way (not the cleanest for sure) would be to create another product, which is a simple product, with the same spec(price,title,desc ecc) as the product w/o selected variation (because after all you still need to give that choice a price to make things work). Then you would only need to hook into the add_to_cart to ignore the error and just add the simple product. That said, this looks more a design problem, maybe you should try to re-think this to not go against Woocommerce nature – Diego Apr 12 '21 at 09:25
  • So your product is a variable product, which have variations, which have prices. A variations is made of a combination of attributes. You want to be able to add a variation to the cart, even if all the attribute have not been selected, and so a unique variation cannot be identified? Where the price would come from? What about the unselected attributes, and if they're not important, why are they here in the first place? Optionnal attributes? – Mtxz Apr 12 '21 at 18:24
  • @Mtxz Both the price and the variation are determined by the selection of one attribute, by a custom plugin. Optional attributes, in essence, is what I need so that this doesn't fail the validation check on mandatory attribute fields. Using a hook, like `woocommerce_add_to_cart_validation`, or a similar, to filter out unfilled attributes prior to validation was my initial thinking. – UncaughtTypeError Apr 12 '21 at 19:07
  • @Diego You're right, this is fundamentally a design flaw of an existing code base. Working with WooCommerce ver 3.6.3, but not ver 5.1.0. – UncaughtTypeError Apr 12 '21 at 19:13
  • @UncaughtTypeError what is the plugin you use? The variation and attribute are not handled by woocommerce? (If I remember right, attributes are taxonomy terms). I would like to have enough to reproduce the product on my side to make tests (number of attributes required, number of not required attributes). As for me, you need a full combination of attributes to have a variations and so a price. – Mtxz Apr 12 '21 at 20:18
  • @UncaughtTypeError I updated my original answer after tests about using non-variation attributes. – Mtxz Apr 19 '21 at 23:32

2 Answers2

3

1 - You could use "variation" attributes, and "not-variation" attributes. UPDATED AFTER TESTS

For the attribute that will handle your product price:

  • create a variable product

  • create real Woocommerce product attributes (real taxonomy and terms) (it won't work with "created on product page" attributes, as it'll not create real taxonomy and terms)

  • Here I created 3 attributes with some terms enter image description here

  • On your variable product, I choose all of my 3 attributes. But specified to only use Color and Size for variations (so Color and Variations will handle my prices variations), not the attribute "optional" (that will be an optional option) enter image description here

  • Then, generate your variations. You can see here that I only have variations for a combination of Color and Size, nothing about the "Optional" attribute yet

  • Also, select the "default values" for your variation attributes. So on the frontend, the attribute select HTML input will have a pre-selected option (user can add to cart directly) enter image description here

  • Now we have our variation attributes, with preselected values on the frontend

  • But we still miss our "optional" attributes

  • Add the following code to your function.php or related (Inspired, updated/refreshed, and adapted from this) (sorry for formatting, snippet also available as gist)

  • This will handle outputting the select input for the optional attribute, saving it to cart and to order. You can adapt it to make it required or not, with a default value or not, edit HTML and placement with different hooks.

/**
 * List available attributes on the product page in a drop-down selection
 */
function list_attributes_on_product_page() {
    global $product;
    $attributes = $product->get_attributes();

    if ( ! $attributes ) {
        return;
    }

    //from original script, but here we want to use it for variable products
    /*if ($product->is_type( 'variable' )) {
        return;
    }*/

    echo '<div style="padding-bottom:15px;">';

    foreach ( $attributes as $attribute ) {

        //If product is variable, and attribute is used for variation: woocommerce already handle this input - so it can also be used with attributes of simple products (not variables)
    if($product->is_type( 'variable' ) && $attribute['variation']) {
        continue;
    }

        //get taxonomy for the attribute - eg: Size
        $taxonomy = get_taxonomy($attribute['name']);

        //get terms - eg: small
        $options = wc_get_product_terms( $product->get_id(), $attribute['name'], array( 'fields' => 'all' ) );
        $label = str_replace('Product ', '', $taxonomy->label);
        //display select input
        ?>
        <div style="padding-bottom:8px;">
            <label for="attribute[<?php echo $attribute['id']; ?>]"><?php echo $label; ?></label>
            <br />
            <!-- add required attribute or not, handle default with "selected" attribute depending your needs -->
            <select name="attribute[<?php echo $attribute['id']; ?>]" id="attribute[<?php echo $attribute['id']; ?>]">
                <option value disabled selected>Choose an option</option>
                <?php foreach ( $options as $pa ): ?>
                    <option value="<?php echo $pa->name; ?>"><?php echo $pa->name; ?></option>
                <?php endforeach; ?>
            </select>
        </div>
        <?php
    }

    echo '</div>';
}
add_action('woocommerce_before_add_to_cart_button', 'list_attributes_on_product_page');

/**
 * Add selected attributes to cart items
 */
add_filter('woocommerce_add_cart_item_data', 'add_attributes_to_cart_item', 10, 3 );
function add_attributes_to_cart_item( $cart_item_data, $product_id, $variation_id ) {
    $attributes = $_POST['attribute'] ?? null;

    if (empty( $attributes ) ) {
        return $cart_item_data;
    }

    $cart_item_data['attributes'] = serialize($attributes);

    return $cart_item_data;
}

/**
 * Display attributes in cart
 */
add_filter( 'woocommerce_get_item_data', 'display_attributes_in_cart', 10, 2 );
function display_attributes_in_cart( $item_data, $cart_item ) {
    if ( empty( $cart_item['attributes'] ) ) {
        return $item_data;
    }

    foreach (unserialize($cart_item['attributes']) as $attributeID => $value) {
        $attribute = wc_get_attribute($attributeID);
        $item_data[] = array(
            'key'     => $attribute->name,
            'value'   => $value,
            'display' => '',
        );
    }

    return $item_data;
}

/**
 * Add attribute data to order items
 */
add_action( 'woocommerce_checkout_create_order_line_item', 'add_attributes_to_order_items', 10, 4 );
function add_attributes_to_order_items( $item, $cart_item_key, $values, $order ) {
    if ( empty( $values['attributes'] ) ) {
        return;
    }

    foreach (unserialize($values['attributes']) as $attributeID => $value) {
        $attribute = wc_get_attribute($attributeID);
        $item->add_meta_data( $attribute->name, $value );
    }
}

Results: enter image description here

2 - Plugins

You could also check with plugins like Product Add-Ons or Extra Product Options (Product Addons) for WooCommerce. In addition to real Woocommerce product variations with your attribute handling prices, those plugins could allow you to add optional fields on the product level when adding it to the cart. It can be enough if you don't need real product variations for those "optional" attributes, but only "optional fields that will be saved to the order line-item product".

3 - with hooks (tweaks)

Now, if you really need to use hooks to handle this issue, and you already have variations for required and optional attributes. So you generated all product variations for all attributes. But in the end, only 1 attribute has a price impact, so many variations have the same price).

You could first try to handle it with "default" attributes values. So by only changing the "required price impacting" attribute, the user always has an existing product variation from the combination of attributes. So it can be added to the cart.

If for some reason, you cannot preselect the default yourself: still create the default attribute term, and hook before add to cart. There, from the POST variables, you could:

  • find the required attribute value, and so find back in your DB the corresponding product variation
  • programmatically add to cart the variation with the correct "required" attribute, and the default "non required" attribute.

The thing is with the variable products: attributes are nothing for the checkout process. When user select options (attributes) on the frontend, he selects a combination of attribute that have or not a corresponding product variation (created by admin, or with woocommerce helper). Woocommerce needs an existing product variation (combination of attributes), that you can see on the product page, to add something to the cart. Those variations are in DB a custom post_type "product_variation" that can be added to the cart and to a order as line-item. If the combination of attributes doesn't have a matching product variation: there's nothing to add to the cart.

Mtxz
  • 2,185
  • 10
  • 20
3

You can try

add_filter('woocommerce_dropdown_variation_attribute_options_args', 'setSelectDefaultVariableOption', 10, 1);
function setSelectDefaultVariableOption($args)
{
    $default = $args['product']->get_default_attributes();
    if (count($args['options']) > 0 && empty($default)) {
        $args['selected'] = $args['options'][0];
    }
    return $args;
}
  • 1
    Can you walk us through what's going on here so that it's clear? Thanks. – UncaughtTypeError Apr 18 '21 at 17:38
  • What part do you need me to explain? – Binh Nguyen Apr 19 '21 at 02:36
  • All the essential parts. The answer itself could be improved with a breakdown of what's going on, for the community as a whole. – UncaughtTypeError Apr 19 '21 at 13:25
  • While testing, I tested this code. I can't get get_default_attributes() to work as expected it seems. What I can say is that the "woocommerce_dropdown_variation_attribute_options_args" filter allows you to edit the configuration that will be used to generate the HTML select and options for the product attributes on frontend. So the goal seems to add the "selected" attribute to one of the option. But as I tested in my answer, woocommerce already handles this natively (default attribute values for product variations) – Mtxz Apr 19 '21 at 23:45