Fixing Magento’s Configurable Attribute Ordering

Somewhere along the way in a 1.9.x update, configurable attribute sorting stopped working on the fronted. Magento should use an attribute’s position parameter (in the options tab when editing a product attribute), to order the dropdown on a product detail page.

The issue appears to be where Magento assigns prices to an attribute collection in Mage_Catalog_Model_Resource_Product_Type_Configurable_Attribute_Collection

Magento first gets the options for the attribute here:

 $options = $productAttribute->getFrontend()->getSelectOptions();

At this point, all is good. In Magento’s SQL to get the options, sort_order is used correctly and the array of options is in the correct order.

The problem seems to occur in the loop on line 257:

 foreach ($this->getProduct()->getTypeInstance(true)
->getUsedProducts(array($productAttribute->getAttributeCode()), $this->getProduct())
   as $associatedProduct) {
...
}

The order of the attributes will now be determined by the order of items in this loop, which is the order of associated products.

What we need to do is re-order the $values array which is populated in the above loop, we can do this using the order of the $options array:

$order = 0;
$optionsByValue = array();
foreach ($options as $option) {
    $optionsByValue[$option['value']] = $option['label'];
    $orders[$option['value']] = $order;
    $order++;
}

Here we utilise the existing loop which populates the $optionsByValue array to log the ordering of each option. We can then use those values by passing them into our custom sort method just before Magento adds the prices to each item:

/* Resort the values array using the order we've logged against options */
usort($values, array($this, 'sortValues'));

foreach ($values as $data) {
    $this->getItemById($data['product_super_attribute_id'])->addPrice($data);
}

Here’s the sort method which puts the array back to the correct sort order:

public function sortValues($value1, $value2){
    if(isset($value1['sort_order']) && isset($value2['sort_order'])){
        if($value1['sort_order'] > $value2['sort_order']){
            return 1;
        }

        if($value1['sort_order'] < $value2['sort_order']) {
            return -1;
        }

        return 0;
    }
}