Need to be able to manually sort attributes by row weight of the product variation with Commerce Fancy Attributes

If you use the Commerce Fancy Attribute module you'll see that your product variations are in a different order than the product attribtues. The solution I'm going to show here is a simple custom code to solve that issue.

The solution is specific to the project and you may be find it useful to create a generic solution too.

/**
 * Implements hook_element_info_alter().
 *
 * Alter the fancy attributes element.
 */
function MODULE_NAME_element_info_alter(&$type) {
  $type['commerce_fancy_attributes']['#process'] = array('MODULE_NAME_fancy_attributes_process_radios');
}

In the element process function we first load the node:

    $nid = $form_state['build_info']['args'][2]['entity_id'];
    $node = node_load($nid);

Then you look up the attributes and product variation delta relationship:

    $node_wrapper = entity_metadata_wrapper('node', $node);
    $products = $node_wrapper->field_product->value();
    $attributes = array();
    foreach($products as $delta => $product){
      if(isset($product->ATTRIBUTE_FIELD) && !empty($product->ATTRIBUTE_FIELD)){
        $attributes[$product->ATTRIBUTE_FIELD[LANGUAGE_NONE][0]['target_id']] = $delta;
        }
      }

Then you change the fancy attribute weight values for the radio button:

$weight = $attributes[$key];

The complete process function:

/**
 * Process function for the commerce_fancy_attributes element.
 *
 * Each radio button gets a description containing the rendered term.
 * This description is hidden by default. Then, if JS is enabled,
 * the radio button and label are hidden, the rendered term in the
 * description is shown and made clickable.
 */
function MODULE_NAME_fancy_attributes_process_radios($element, &$form_state, $complete_form = NULL) {
  if (isset($element['#options']) && count($element['#options']) > 0) {
    $tids = array_keys($element['#options']);
    $terms = entity_load('taxonomy_term', $tids);
    $render_terms = entity_view('taxonomy_term', $terms, 'add_to_cart_form');
    $render_terms = $render_terms['taxonomy_term'];
    $nid = $form_state['build_info']['args'][2]['entity_id'];
    $node = node_load($nid);
    $node_wrapper = entity_metadata_wrapper('node', $node);
    $products = $node_wrapper->field_product->value();
    $attributes = array();
    foreach($products as $delta => $product){
      if(isset($product->ATTRIBUTE_FIELD) && !empty($product->ATTRIBUTE_FIELD)){
        $attributes[$product->ATTRIBUTE_FIELD[LANGUAGE_NONE][0]['target_id']] = $delta;
        }
      }
    $weight = 0;
    foreach ($element['#options'] as $key => $choice) {
      // Maintain order of options as defined in #options, in case the element
      // defines custom option sub-elements, but does not define all option
      // sub-elements.
      // $weight += 0.001;
      $weight = $attributes[$key];
      $element += array($key => array());
      // Generate the parents as the autogenerator does, so we will have a
      // unique id for each radio button.
      $parents_for_id = array_merge($element['#parents'], array($key));
      // Render only the visible children of the render array, to prevent
      // unneeded markup.
      $render = array();
      foreach (element_get_visible_children($render_terms[$key]) as $child) {
        $render[$child] = $render_terms[$key][$child];
      }
      $output = drupal_render($render);

      $default_value = isset($element['#default_value']) ? $element['#default_value'] : NULL;
      // Wrap the description with a div specifying the term ID.
      $class = array('term-' . $key);
      if ($key == $default_value) {
        // Add a class specifying this is the currently selected radio.
        $class[] = 'description-selected';
      }
      $output = '
' . $output . '
'; $element[$key] += array( '#type' => 'radio', '#title' => $choice, // The key is sanitized in drupal_attributes() during output from the // theme function. '#return_value' => $key, '#default_value' => $default_value, '#attributes' => $element['#attributes'], '#parents' => $element['#parents'], '#id' => drupal_html_id('edit-' . implode('-', $parents_for_id)), '#ajax' => isset($element['#ajax']) ? $element['#ajax'] : NULL, '#weight' => $weight, '#description' => $output, ); } } return $element; }