Just to add my two cents, the other two answers did well to point me in fix's direction, but I thought I'd attack it at the source rather than the block presentation point.
You can achieve the same result by extending the Mage_Catalog_Model_Resource_Product_Type_Configurable_Attribute_Collection
model's _loadPrices()
method, which despite the name is where a change was made (presumably for performance) resulting in the attributes being ordered by ID rather than by relevance.
The change appears to have been made to avoid nested foreach
statements, but in turn loses the correct order as well. This solution modifies the updated logic slightly to track the attribute options, then performs another loop based on the original order to actually do the adding.
Here's an adjusted walkthrough similar to meogi's answer above:
Step 1: Register a new module
Note: if you already have one, re-use an existing one.
# File: app/etc/modules/YourCompany_AttributeFix.xml
<?xml version="1.0"?>
<config>
<modules>
<YourCompany_AttributeFix>
<active>true</active>
<codePool>local</codePool>
<depends>
<Mage_Catalog />
</depends>
</YourCompany_AttributeFix>
</modules>
</config>
Step 2: Create the module's configuration
# File: app/code/local/YourCompany/AttributeFix/etc/config.xml
<?xml version="1.0"?>
<config>
<modules>
<YourCompany_AttributeFix>
<version>0.1.0</version>
</YourCompany_AttributeFix>
</modules>
<global>
<models>
<catalog_resource>
<rewrite>
<product_type_configurable_attribute_collection>YourCompany_AttributeFix_Model_Resource_Product_Type_Configurable_Attribute_Collection</product_type_configurable_attribute_collection>
</rewrite>
</catalog_resource>
</models>
</global>
</config>
Step 3: Add the resource model extension
# File: app/code/local/YourCompany/AttributeFix/Model/Resource/Product/Type/Configurable/Attribute/Collection.php
/**
* Catalog Configurable Product Attribute Collection - overridden to re-enable the attribute option
* sorting by relevance rather than by ID as changed in the Magento core class
*/
class YourCompany_AttributeFix_Model_Resource_Product_Type_Configurable_Attribute_Collection
extends Mage_Catalog_Model_Resource_Product_Type_Configurable_Attribute_Collection
{
/**
* Load attribute prices information
*
* @return Mage_Catalog_Model_Resource_Product_Type_Configurable_Attribute_Collection
*/
protected function _loadPrices()
{
if ($this->count()) {
$pricings = array(
0 => array()
);
if ($this->getHelper()->isPriceGlobal()) {
$websiteId = 0;
} else {
$websiteId = (int)Mage::app()->getStore($this->getStoreId())->getWebsiteId();
$pricing[$websiteId] = array();
}
$select = $this->getConnection()->select()
->from(array('price' => $this->_priceTable))
->where('price.product_super_attribute_id IN (?)', array_keys($this->_items));
if ($websiteId > 0) {
$select->where('price.website_id IN(?)', array(0, $websiteId));
} else {
$select->where('price.website_id = ?', 0);
}
$query = $this->getConnection()->query($select);
while ($row = $query->fetch()) {
$pricings[(int)$row['website_id']][] = $row;
}
$values = array();
foreach ($this->_items as $item) {
$productAttribute = $item->getProductAttribute();
if (!($productAttribute instanceof Mage_Eav_Model_Entity_Attribute_Abstract)) {
continue;
}
$options = $productAttribute->getFrontend()->getSelectOptions();
$optionsByValue = array();
foreach ($options as $option) {
$optionsByValue[$option['value']] = $option['label'];
}
/**
* Modification to re-enable the sorting by relevance for attribute options
* @author Robbie Averill <[email protected]>
*/
$toAdd = array();
foreach ($this->getProduct()->getTypeInstance(true)
->getUsedProducts(array($productAttribute->getAttributeCode()), $this->getProduct())
as $associatedProduct) {
$optionValue = $associatedProduct->getData($productAttribute->getAttributeCode());
if (array_key_exists($optionValue, $optionsByValue)) {
$toAdd[] = $optionValue;
}
}
// Add the attribute options, but in the relevant order rather than by ID
foreach (array_intersect_key($optionsByValue, array_flip($toAdd)) as $optionValueKey => $optionValue) {
// If option available in associated product
if (!isset($values[$item->getId() . ':' . $optionValue])) {
// If option not added, we will add it.
$values[$item->getId() . ':' . $optionValueKey] = array(
'product_super_attribute_id' => $item->getId(),
'value_index' => $optionValueKey,
'label' => $optionsByValue[$optionValueKey],
'default_label' => $optionsByValue[$optionValueKey],
'store_label' => $optionsByValue[$optionValueKey],
'is_percent' => 0,
'pricing_value' => null,
'use_default_value' => true
);
}
}
/**
* End attribute option order modification
* @author Robbie Averill <[email protected]>
*/
}
foreach ($pricings[0] as $pricing) {
// Addding pricing to options
$valueKey = $pricing['product_super_attribute_id'] . ':' . $pricing['value_index'];
if (isset($values[$valueKey])) {
$values[$valueKey]['pricing_value'] = $pricing['pricing_value'];
$values[$valueKey]['is_percent'] = $pricing['is_percent'];
$values[$valueKey]['value_id'] = $pricing['value_id'];
$values[$valueKey]['use_default_value'] = true;
}
}
if ($websiteId && isset($pricings[$websiteId])) {
foreach ($pricings[$websiteId] as $pricing) {
$valueKey = $pricing['product_super_attribute_id'] . ':' . $pricing['value_index'];
if (isset($values[$valueKey])) {
$values[$valueKey]['pricing_value'] = $pricing['pricing_value'];
$values[$valueKey]['is_percent'] = $pricing['is_percent'];
$values[$valueKey]['value_id'] = $pricing['value_id'];
$values[$valueKey]['use_default_value'] = false;
}
}
}
foreach ($values as $data) {
$this->getItemById($data['product_super_attribute_id'])->addPrice($data);
}
}
return $this;
}
}
Step 4: Clear your cache
For reference, the actual change to the core class in a git diff
would be below (do not directly edit core files!):
diff --git a/app/code/core/Mage/Catalog/Model/Resource/Product/Type/Configurable/Attribute/Collection.php b/app/code/core/Mage/Catalog/Model/Resource/Product/Type/Configurable/Attribute/Collection.php
index 135d9d3..4d2a59b 100644
--- a/app/code/core/Mage/Catalog/Model/Resource/Product/Type/Configurable/Attribute/Collection.php
+++ b/app/code/core/Mage/Catalog/Model/Resource/Product/Type/Configurable/Attribute/Collection.php
@@ -254,6 +254,11 @@ class Mage_Catalog_Model_Resource_Product_Type_Configurable_Attribute_Collection
$optionsByValue[$option['value']] = $option['label'];
}
+ /**
+ * Modification to re-enable the sorting by relevance for attribute options
+ * @author Robbie Averill <[email protected]>
+ */
+ $toAdd = array();
foreach ($this->getProduct()->getTypeInstance(true)
->getUsedProducts(array($productAttribute->getAttributeCode()), $this->getProduct())
as $associatedProduct) {
@@ -261,22 +266,31 @@ class Mage_Catalog_Model_Resource_Product_Type_Configurable_Attribute_Collection
$optionValue = $associatedProduct->getData($productAttribute->getAttributeCode());
if (array_key_exists($optionValue, $optionsByValue)) {
- // If option available in associated product
- if (!isset($values[$item->getId() . ':' . $optionValue])) {
- // If option not added, we will add it.
- $values[$item->getId() . ':' . $optionValue] = array(
- 'product_super_attribute_id' => $item->getId(),
- 'value_index' => $optionValue,
- 'label' => $optionsByValue[$optionValue],
- 'default_label' => $optionsByValue[$optionValue],
- 'store_label' => $optionsByValue[$optionValue],
- 'is_percent' => 0,
- 'pricing_value' => null,
- 'use_default_value' => true
- );
- }
+ $toAdd[] = $optionValue;
}
}
+
+ // Add the attribute options, but in the relevant order rather than by ID
+ foreach (array_intersect_key($optionsByValue, array_flip($toAdd)) as $optionValueKey => $optionValue) {
+ // If option available in associated product
+ if (!isset($values[$item->getId() . ':' . $optionValue])) {
+ // If option not added, we will add it.
+ $values[$item->getId() . ':' . $optionValueKey] = array(
+ 'product_super_attribute_id' => $item->getId(),
+ 'value_index' => $optionValueKey,
+ 'label' => $optionsByValue[$optionValueKey],
+ 'default_label' => $optionsByValue[$optionValueKey],
+ 'store_label' => $optionsByValue[$optionValueKey],
+ 'is_percent' => 0,
+ 'pricing_value' => null,
+ 'use_default_value' => true
+ );
+ }
+ }
+ /**
+ * End attribute option order modification
+ * @author Robbie Averill <[email protected]>
+ */
}
foreach ($pricings[0] as $pricing) {
This is also on GitHub if anyone wants it for reference.
Edit: I've also logged this as a bug with Magento.