Adding price to product options dropdown in WooCommerce

[Edit: This solution doesn’t work with WooCommerce 2.5.x+ – see Andur Indrason’s comment below for a possible solution ]

This is how the end result will look like:
variable_product_price

 

The code

Tested and working with WooCommerce v2.3.7

The first block goes to functions.php.

// functions.php

/**
 * Override WC default add to cart template function to add some extra data for products with single variation
 */
function woocommerce_variable_add_to_cart() {
  global $product;

  // Enqueue variation scripts
  wp_enqueue_script( 'wc-add-to-cart-variation' );

  $available_variations = $product->get_available_variations();
  $attributes = $product->get_variation_attributes();
  $selected_attributes = $product->get_variation_default_attributes();

  $attributes_meta = codelight_get_single_attribute_variation_price($available_variations, $attributes);

  // Load the template
  wc_get_template( 'single-product/add-to-cart/variable.php', array(
      'available_variations' => $available_variations,
      'attributes'           => $attributes,
      'selected_attributes'  => $selected_attributes,
      'attributes_meta'      => $attributes_meta
    ) );
}

/**
 * Get attribute metadata containing matching (single) variation id, title and price
 */
function codelight_get_single_attribute_variation_price($available_variations, $attributes) {

  // Make sure the variation depends on just a single attribute
  // as otherwise the final price would depend on more than one attribute
  // so it's logically not possible to display it for a single attribute
  if (count($attributes) !== 1) {
    return array();
  }

  // Store the price and some additional metadata here
  $attribute_metadata = array();

  // Attributes are stored in a multidimensional array with the key being the attribute label
  foreach ($attributes as $attribute_options) {
    foreach ($attribute_options as $attribute) {

      // Use the title/slug to match attributes to variations since attributes don't seem to have an ID?
      $attribute_slug = sanitize_title($attribute);

      // Match attribute with variation and its price
      foreach ($available_variations as $available_variation) {
        if (in_array($attribute_slug, $available_variation['attributes'])) {
          $metadata[] = array(
            'attribute_title' => $attribute,
            'attribute_slug' => $attribute_slug,
            'variation_id' => $available_variation['variation_id'],
            'variation_price' => $available_variation['display_price']
          );
        }

      }

    }
  }

  return $metadata;
}

/**
 * Match attribute with variation, return price
 */
function codelight_display_single_attribute_variation_price($name, $attributes_meta) {

  if (empty($attributes_meta)) {
    return;
  }

  foreach ($attributes_meta as $meta) {
    if ($name == $meta['attribute_title']) {

      $price = wc_price( $meta['variation_price'] );
      return " ({$price})";
    }
  }
}

The next blocks go in the template file which should be located in wp-content/your_theme/woocommerce/single-product/add-to-cart/variable.php.
As always, if you don’t have this file, copy it over from wp-content/plugins/woocommerce/templates/single-product/add-to-cart/variable.php. Never modify WooCommerce plugin files.

// single-product/add-to-cart/variable.php line 48
echo 'slug ) . '" ' . selected( sanitize_title( $selected_value ), sanitize_title( $term->slug ), false ) . '>' . apply_filters( 'woocommerce_variation_option_name', $term->name ) . codelight_display_single_attribute_variation_price($term->name, $attributes_meta) . '';
// single-product/add-to-cart/variable.php line 54
echo '' . esc_html( apply_filters( 'woocommerce_variation_option_name', $option ) ) . codelight_display_single_attribute_variation_price($option, $attributes_meta) . '';

Details & usage

This feature only works with variations that have a single attribute. If the price of the product depends on the combination of two (or more) attributes, then it’s logically not possible to display the price in just one dropdown. On product pages with more than one attribute, nothing will happen.

In terms of usability you’ll want to use this feature only if your products don’t have more than one attribute across your whole site. Otherwise your product pages will look and feel inconsistent – it’s confusing for the average user if your attribute dropdowns display the price on some product pages and don’t do that on other product pages.

Code explained

Disclaimer: this is somewhat hacky. WooCommerce doesn’t really provide the tools to do this properly (and rightfully so). Also, it seems attributes and variations aren’t really tied to each other in a way that makes sense, so this solution is error-prone and might break easily with WooCommerce updates. But right now it works nicely. Let’s go over the code:

First of all, we override woocommerce_variable_add_to_cart(). This is the function that renders the add-to-cart block on variable product pages.

wc_get_template( 'single-product/add-to-cart/variable.php', array(
      'available_variations' => $available_variations,
      'attributes'           => $attributes,
      'selected_attributes'  => $selected_attributes,
      'attributes_meta'      => $attributes_meta
    ) );

What happens here is that the template file is included and the array of data in the second argument is made available for that template. The function is almost exactly the same as the original, except that we also fetch some metadata and store it in $attributes_meta using the following function:

/**
 * Get attribute metadata containing matching (single) variation id, title and price
 */
function codelight_get_single_attribute_variation_price($available_variations, $attributes) {

  // Make sure the variation depends on just a single attribute
  // as otherwise the final price would depend on more than one attribute
  // so it's logically not possible to display it for a single attribute
  if (count($attributes) !== 1) {
    return array();
  }

  // Store the price and some additional metadata here
  $attribute_metadata = array();

  // Attributes are stored in a multidimensional array with the key being the attribute label
  foreach ($attributes as $attribute_options) {
    foreach ($attribute_options as $attribute) {

      // Use the title/slug to match attributes to variations since attributes don't seem to have an ID?
      $attribute_slug = sanitize_title($attribute);

      // Match attribute with variation and its price
      foreach ($available_variations as $available_variation) {
        if (in_array($attribute_slug, $available_variation['attributes'])) {
          $metadata[] = array(
            'attribute_title' => $attribute,
            'attribute_slug' => $attribute_slug,
            'variation_id' => $available_variation['variation_id'],
            'variation_price' => $available_variation['display_price']
          );
        }

      }

    }
  }

  return $metadata;
}

We first check if the current product has more than one attribute. If it does, then quit.

  if (count($attributes) !== 1) {
    return array();
  }

As explained in the beginning of this post, this feature makes no sense in the context of more than one attribute.

Next we match the product attributes with variations. This is done by looping over all the attributes and checking if the variation has an attribute with the same name as the current attribute of the loop. If it does, then we have a match and we can add the variation price to the attribute.

// Match attribute with variation and its price
foreach ($available_variations as $available_variation) {
    if (in_array($attribute_slug, $available_variation['attributes'])) {
      $metadata[] = array(
        'attribute_title' => $attribute,
        'attribute_slug' => $attribute_slug,
        'variation_id' => $available_variation['variation_id'],
        'variation_price' => $available_variation['display_price']
      );
    }

}

Finally, in the template we call:

codelight_display_single_attribute_variation_price($option, $attributes_meta)

Which simply fetches the price of the attribute that has the same name as the current attribute in the loop

function codelight_display_single_attribute_variation_price($name, $attributes_meta) {

  if (empty($attributes_meta)) {
    return;
  }

  foreach ($attributes_meta as $meta) {
    if ($name == $meta['attribute_title']) {

      $price = wc_price( $meta['variation_price'] );
      return " ({$price})";
    }
  }
}

 

Not very pretty, but does the job.

Indrek Kõnnussaar

I'm a veteran Wordpress developer, context-driven tester, security enthusiast and the mastermind behind Codelight. I love building stuff that works and fixing stuff that doesn't.

Write me directly indrek@codelight.eu

6 Responses to “Adding price to product options dropdown in WooCommerce”

  1. Dale Jacobs

    Clearer..
    add_filter( ‘woocommerce_variation_option_name’, ‘display_price_in_variation_option_name’ );

    function display_price_in_variation_option_name( $term ) {
    global $wpdb, $product;

    $result = $wpdb->get_col( “SELECT slug FROM {$wpdb->prefix}terms WHERE name = ‘$term'” );

    $term_slug = ( !empty( $result ) ) ? $result[0] : $term;

    $query = “SELECT postmeta.post_id AS product_id
    FROM {$wpdb->prefix}postmeta AS postmeta
    LEFT JOIN {$wpdb->prefix}posts AS products ON ( products.ID = postmeta.post_id )
    WHERE postmeta.meta_key LIKE ‘attribute_%’
    AND postmeta.meta_value = ‘$term_slug’
    AND products.post_parent = $product->id”;

    $variation_id = $wpdb->get_col( $query );

    $parent = wp_get_post_parent_id( $variation_id[0] );

    if ( $parent > 0 ) {
    $_product = new WC_Product_Variation( $variation_id[0] );
    return $term . ‘ (‘ . woocommerce_price( $_product->get_price() ) . ‘)’;
    }
    return $term;

    }

    Reply
  2. Sam

    Doesn’t Really work with the latest version anymore..

    Reply
  3. Andur Indrason

    Thank you very much for your great snippet that worked a treat for a while. Sadly it stopped working and it does not seem to work for variations with more than one property or dropdown. Just wanted to let you know that we switched to a plugin that can do just that: https://codecanyon.net/item/woocommerce-variation-price-hints/18772255 . It displays prices or price differences even when prices depend on more than one attribute. I hope someone looking for something like this will find this as useful as your snippet was for me. Cheers Andur.

    Reply
    • Indrek Kõnnussaar

      Thanks for the link Andur!

      Reply
  4. innocent

    Dale Jacobs code is not working for woocommerce recent version, any one solution

    Reply

Leave a Reply

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×