23

We have implemented the new inbuilt Swatches feature in our latest new site build When we enable the swatches on category pages the page load time goes from 2 seconds to 38 + seconds.

I was wondering if anyone else had had this issue and if so could give us an indication of any possible solutions?

We have tried EE 1.14.1 and CE 1.9.1 with 36 configurable products with swatches applied on the standard rwd theme and no other modules active.

This issue can't be resolved by caching as every time a user searches or filters a category the page grinds to a halt again.

2
  • I cannot reproduce this. Please give us some more direction on the type of plugins installed, theme etc. Please follow the Magento debugging process by disabling your theme, disabling local modules and re-try.
    – philwinkle
    Commented Feb 13, 2015 at 19:29
  • The attributes we are using are colour swatches and sizes no more than 8 per item and in most cases no more than 4. This is being run on a blank magento CE 1.9.1 installation with sample data loaded and 10 configurable products with the custom swatches added. Its definitely associated with the swatches as the more we add the slower the site gets. Please note that caching is turned off to test this as users can filter the search and we cant have a crazy load time each time a user tweaks their search. Thanks for your time :) Commented Feb 16, 2015 at 9:01

2 Answers 2

22

Right. I detect problem on Mage_ConfigurableSwatches_Helper_Mediafallback::attachConfigurableProductChildrenAttributeMapping function.

I make some changes on it. This increase performance.

Try:

  1. Copy /app/code/core/Mage/ConfigurableSwatches/Helper/Mediafallback.php to /app/code/local/Mage/ConfigurableSwatches/Helper/Mediafallback.php.

  2. On /app/code/local/Mage/ConfigurableSwatches/Helper/Mediafallback.php file move this code (ll.88-91)

     // 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);
    

    up to before the foreach loop.

This is the changed method:

 /**
 * 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();
    }

    // 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);

    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;
                }

                // 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']);
        }

        $parentProduct->setChildAttributeLabelMapping($mapping)
            ->setListSwatchAttrValues($listSwatchValues);
    } // end looping parent products
}
4
  • I was having the same problem with swatches enabled on the list pages and this helped speed things up considerably, so thanks! Commented Jun 4, 2015 at 17:33
  • I found the same problem. resolving it took the page load from 2.5 minutes to 7 seconds. Commented Jun 19, 2015 at 6:14
  • These swatches really slow down the categories especially when you have a lot of confugurable products. The solution of Андрей М. cut down the loading from 10 to 3 seconds on a category full with configurable products! Thank you! Commented Aug 11, 2015 at 9:48
  • +1! Thanks for sharing this. We are using a lot of configurable with several options each and just couldn't use color swatches anymore...
    – Marc
    Commented Feb 22, 2016 at 11:39
4

Additional way to improve performance configurable swatches when you have a lot of attribute options.

For example if you have 2000 options and show 36 products in catalog list, in this case method Mage_ConfigurableSwatches_Model_Resource_Catalog_Product_Attribute_Super_Collection::_loadOptionLabels() will join to each super_attributes option labels and you will get 2000 * 36 = 72000 rows.

I have rewritten this method and it loads only 2000 rows instead 72000

<?php
/**
 * Load attribute option labels for current store and default (fallback)
 *
 * @return $this
 */
protected function _loadOptionLabels()
{
    if ($this->count()) {
        $labels = $this->_getOptionLabels();
        foreach ($this->getItems() as $item) {
            $item->setOptionLabels($labels);
        }
    }
    return $this;
}

/**
 * Get Option Labels
 *
 * @return array
 */
protected function _getOptionLabels()
{
    $attributeIds = $this->_getAttributeIds();

    $select = $this->getConnection()->select();
    $select->from(array('options' => $this->getTable('eav/attribute_option')))
        ->join(
            array('labels' => $this->getTable('eav/attribute_option_value')),
            'labels.option_id = options.option_id',
            array(
                'label' => 'labels.value',
                'store_id' => 'labels.store_id',
            )
        )
        ->where('options.attribute_id IN (?)', $attributeIds)
        ->where(
            'labels.store_id IN (?)',
            array(Mage_Catalog_Model_Abstract::DEFAULT_STORE_ID, $this->getStoreId())
        );

    $resultSet = $this->getConnection()->query($select);
    $labels = array();
    while ($option = $resultSet->fetch()) {
        $labels[$option['option_id']][$option['store_id']] = $option['label'];
    }
    return $labels;
}

/**
 * Get Attribute IDs
 *
 * @return array
 */
protected function _getAttributeIds()
{
    $attributeIds = array();
    foreach ($this->getItems() as $item) {
        $attributeIds[] = $item->getAttributeId();
    }
    $attributeIds = array_unique($attributeIds);

    return $attributeIds;
}

Not the answer you're looking for? Browse other questions tagged or ask your own question.