Woocommerce: use non-variation attributes from frontend – allow customers to select term (option) from non-variation attributes on variable or simple products frontend pages, add selection to cart item, display it in cart, add it to order item.
/** * Inspired from: https://gist.github.com/dazecoop/548b2621e86fb030da8e5adb1bfe484f * * How to use: * - add to your function.php or related * * What you can use it for: * Variable products: * - you have variable product with some attributes not used for variations * - you still want to let your users choose some of those attributes values on frontend * - eg: your have an attribute that don't impact prices, and that is required or not, and don't want to create useless variations for those attributes * * Simple product: * - you have a single product, and want to allow user to select some of its attributes values from frontend */ /** * List available attributes on product page in a drop-down selection */ function list_attributes_on_product_page() { global $product; $attributes = $product->get_attributes(); if ( ! $attributes ) { return; } //uncomment this if you only want fields to be displayed for non-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 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>'; } //update the "woocommerce_before_add_to_cart_button" to change the position of the HTML output 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 ); } }