Search code examples
phphtmlwoocommercetaxonomy-termsproduct-variations

Specific product attribute custom dropdown for WooCommerce variation selection


In WooCommerce, I have an attribute called License Type. This attribute has 2 terms:

  1. Single Site License
  2. Multi Site License

With the above, I'm trying to create a custom select dropdown, where the option contains the variation ID.

For example, my product has both of those variations assigned. For that product:

  1. Single Site License has an ID of 210
  2. Multi Site License has an ID of 211

So, I'm looking for my output to look something like this:

<select name="pa_license-type" id="pa_license-type">
  <option value="single-site-license" data-variation-id="210">Single Site License</option>
  <option value="multi-site-license" data-variation-id="211">Multi Site License</option>
</select>

To make this dynamic, for each product, I've attempted the following:

if ($product->is_type('variable')) :
  $product_id = $product->get_id();
  $available_variations = $product->get_children();

  $attributes = $product->get_variation_attributes();

  foreach ($attributes as $attribute_name => $options) :
      $taxonomy = str_replace('attribute_', '', $attribute_name);
    $terms = get_terms(array(
      'taxonomy' => $taxonomy,
      'hide_empty' => false,
    ));

    if (!is_wp_error($terms) && !empty($terms)) :

      $attribute_label = wc_attribute_label($taxonomy);
      echo '<label for="' . esc_attr($taxonomy) . '">' . esc_html($attribute_label) . '</label>';
      echo '<select name="' . esc_attr($taxonomy) . '" id="' . esc_attr($taxonomy) . '">';

      $variation_ids_map = array();

      foreach ($terms as $term) :
          $term_slug = $term->slug;
          $current_variation_id = '';

          foreach ($available_variations as $variation_id_candidate) :
            $variation = wc_get_product($variation_id_candidate);
            if ($variation && has_term($term_slug, $taxonomy, $variation->get_id())) :
              $current_variation_id = $variation->get_id();
              break;
            endif;
          endforeach;
          
          $variation_ids_map[$term_slug] = $current_variation_id;
          echo '<option value="' . esc_attr($term_slug) . '" data-variation-id="' . esc_attr($current_variation_id) . '">' . esc_html($term->name) . '</option>';

      endforeach;

      echo '</select>';
    endif;

  endforeach;
endif;
?>

But that yields:

<select name="pa_license-type" id="pa_license-type">
  <option value="single-site-license" data-variation-id="">Single Site License</option>
  <option value="multi-site-license" data-variation-id="">Multi Site License</option>
</select>

Any ideas on where I'm going wrong?

Images of backend if reference needed:

enter image description here

enter image description here


Solution

  • There are some mistakes and complications. Try the following simplified code version:

    global $product; // If needed
    
    if ( $product->is_type('variable') ) {
        
        $taxonomy = 'pa_license-type'; // Targeted global product attribute taxonomy
        $terms    = array(); // Initializing
    
        // Loop through available variations
        foreach ( $product->get_children() as $variation_id ) {
            $variation = wc_get_product( $variation_id ); // Get the product variation object
    
            if ( $variation && $variation->exists() && $variation->variation_is_visible() ) {
                if ( $term = get_term_by('name', esc_html( $variation->get_attribute($taxonomy) ), $taxonomy) ) {
                    $terms[$variation->get_id()] = $term;
                }
            }
        }
    
        if ( count($terms) > 0 ) {
            printf('<label for="%s">%s</label><select name="%s" id="%s">', 
                esc_attr($taxonomy), wc_attribute_label($taxonomy), esc_attr($taxonomy), esc_attr($taxonomy) );
    
            foreach ( $terms as $variation_id => $term ) {
                printf('<option value="%s" data-variation-id="%d">%s</option>', esc_attr($term->slug), $variation_id, esc_html($term->name) );
            }
            echo '</select>';
        }
    }
    

    It should work.