Skip to content
This repository has been archived by the owner on Mar 22, 2022. It is now read-only.

Commit

Permalink
Merge pull request #13 from robbieaverill/issue/12-swatch-order
Browse files Browse the repository at this point in the history
[#12] Fix for swatch ordering
  • Loading branch information
robbieaverill committed Jan 17, 2016
2 parents 07d5404 + e41ce1c commit 48d1071
Show file tree
Hide file tree
Showing 15 changed files with 502 additions and 120 deletions.
1 change: 0 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
language: php
php:
- 5.3
- 5.4
- 5.5
- 5.6
Expand Down
7 changes: 6 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,15 @@ You can use this extension module to restore to relevance ordering until the cor
## Notes:

* Updated to work correctly on Magento CE 1.9.2
* Conflict possible with the ConfigurableSwatches module recently introduced since it references the collection resource class directly via hierarchy
* Uses traits, so no longer supports PHP 5.3 or lower. Minimum 5.4 required.

## Changelog:

### 1.1.1

* Fix #12 - added support for ordering swatch options
* Removed support for PHP 5.3 as we use traits now

### 1.0.1

* #10 - added `composer.json` for composer installation.
Expand Down
55 changes: 55 additions & 0 deletions app/code/local/RobbieAverill/AttributeFix/Helper/Data.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
<?php
/**
* Helper to provide sorting methods for attribute option values
*
* @category Mage
* @package RobbieAverill_AttributeFix
* @author Robbie Averill <[email protected]>
*/
class RobbieAverill_AttributeFix_Helper_Data extends Mage_Core_Helper_Abstract
{
/**
* Sort the attribute options by their sort order
* @param array $options
* @return array
*/
public function sortOptionValues($options)
{
$optionIds = array_keys($options);
$sortOrder = $this->getSortOrderForAttributeOptions($optionIds);

foreach ($sortOrder as $optionId => &$value) {
$value = $options[$optionId];
}

return $sortOrder;
}

/**
* Given an array of attribute option IDs, get their sort order
* @param array $optionIds
* @return array
*/
public function getSortOrderForAttributeOptions($optionIds)
{
$resource = Mage::getModel('core/resource');
$connection = $resource->getConnection('core/read');

$select = $connection->select()
->from(
['e' => $resource->getTableName('eav/attribute_option')],
['option_id', 'sort_order']
)
->where('option_id IN (' . implode(',', array_map('intval', $optionIds)) . ')')
->order('sort_order');

$orders = $connection->fetchAll($select);

$output = [];
foreach ($orders as $order) {
$output[$order['option_id']] = $order['sort_order'];
}

return $output;
}
}
114 changes: 114 additions & 0 deletions app/code/local/RobbieAverill/AttributeFix/Helper/Mediafallback.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
<?php
/**
* Extending the mediafallback helper to add sort_order for attribute option values
*
* @category Mage
* @package RobbieAverill_AttributeFix
* @author Robbie Averill <[email protected]>
*/
class RobbieAverill_AttributeFix_Helper_Mediafallback extends Mage_ConfigurableSwatches_Helper_Mediafallback
{
/**
* Set child_attribute_label_mapping on products with attribute label -> product mapping
* Depends on following product data:
* - product must have children products attached
*
* @param array $parentProducts
* @param $storeId
* @return void
*/
public function attachConfigurableProductChildrenAttributeMapping(array $parentProducts, $storeId)
{
$listSwatchAttr = Mage::helper('configurableswatches/productlist')->getSwatchAttribute();

$parentProductIds = array();
/* @var $parentProduct Mage_Catalog_Model_Product */
foreach ($parentProducts as $parentProduct) {
$parentProductIds[] = $parentProduct->getId();
}

$configAttributes = Mage::getResourceModel('configurableswatches/catalog_product_attribute_super_collection')
->addParentProductsFilter($parentProductIds)
->attachEavAttributes()
->setStoreId($storeId)
;

$optionLabels = array();
foreach ($configAttributes as $attribute) {
$optionLabels += $attribute->getOptionLabels();
}

foreach ($parentProducts as $parentProduct) {
$mapping = array();
$listSwatchValues = array();

/* @var $attribute Mage_Catalog_Model_Product_Type_Configurable_Attribute */
foreach ($configAttributes as $attribute) {
/* @var $childProduct Mage_Catalog_Model_Product */
if (!is_array($parentProduct->getChildrenProducts())) {
continue;
}

foreach ($parentProduct->getChildrenProducts() as $childProduct) {

// product has no value for attribute, we can't process it
if (!$childProduct->hasData($attribute->getAttributeCode())) {
continue;
}
$optionId = $childProduct->getData($attribute->getAttributeCode());

// if we don't have a default label, skip it
if (!isset($optionLabels[$optionId][0])) {
continue;
}

// normalize to all lower case before we start using them
$optionLabels = array_map(function ($value) {
return array_map('Mage_ConfigurableSwatches_Helper_Data::normalizeKey', $value);
}, $optionLabels);

// using default value as key unless store-specific label is present
$optionLabel = $optionLabels[$optionId][0];
if (isset($optionLabels[$optionId][$storeId])) {
$optionLabel = $optionLabels[$optionId][$storeId];
}

// initialize arrays if not present
if (!isset($mapping[$optionLabel])) {
$mapping[$optionLabel] = array(
'product_ids' => array(),
);
}
$mapping[$optionLabel]['product_ids'][] = $childProduct->getId();
$mapping[$optionLabel]['label'] = $optionLabel;
$mapping[$optionLabel]['default_label'] = $optionLabels[$optionId][0];
$mapping[$optionLabel]['labels'] = $optionLabels[$optionId];

if ($attribute->getAttributeId() == $listSwatchAttr->getAttributeId()
&& !in_array($mapping[$optionLabel]['label'], $listSwatchValues)
) {
$listSwatchValues[$optionId] = $mapping[$optionLabel]['label'];
}
} // end looping child products
} // end looping attributes


foreach ($mapping as $key => $value) {
$mapping[$key]['product_ids'] = array_unique($mapping[$key]['product_ids']);
}

/**
* Start modification: sort the swatch values by their sort order
* @author Robbie Averill <[email protected]>
*/
$listSwatchValues = Mage::helper('attributefix')->sortOptionValues($listSwatchValues);
/**
* End moficiation
* @author Robbie Averill <[email protected]>
*/

$parentProduct->setChildAttributeLabelMapping($mapping)
->setListSwatchAttrValues($listSwatchValues);
} // end looping parent products
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?php
/**
* Overridden to re-enable the attribute option sorting by relevance rather than by ID as changed
* in the Magento core class. This class is to re-enable the ordering for swatches, since it extends
* the attribute collection by class name.
*
* @category Mage
* @package RobbieAverill_AttributeFix
* @author Robbie Averill <[email protected]>
*/
class RobbieAverill_AttributeFix_Model_Resource_Catalog_Product_Attribute_Super_Collection
extends Mage_ConfigurableSwatches_Model_Resource_Catalog_Product_Attribute_Super_Collection
{
/**
* Get the override functionality - it's in a trait for re-usability
*/
use RobbieAverill_AttributeFix_Model_Resource_Product_Type_Configurable_Attribute_CollectionCommon;
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,120 +11,7 @@ class RobbieAverill_AttributeFix_Model_Resource_Product_Type_Configurable_Attrib
extends Mage_Catalog_Model_Resource_Product_Type_Configurable_Attribute_Collection
{
/**
* Load attribute prices information
*
* @return self
* Get the override functionality - it's in a trait for re-usability
*/
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() . ':' . $optionValueKey])) {
// 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;
}
use RobbieAverill_AttributeFix_Model_Resource_Product_Type_Configurable_Attribute_CollectionCommon;
}
Loading

0 comments on commit 48d1071

Please sign in to comment.