Clasificación de atributos del producto configurable Magento 1.9.1

24

Como ya mencioné, parece haber un problema con magento 1.9.1 y la clasificación de los atributos de los productos configurables. Las opciones de un producto configurable ahora SIEMPRE dependen de la ID del producto simple. Se ignora el orden de las opciones de atributo.

Regresé a magento 1.9.0.1. Quizás alguien pueda determinar cómo se realiza la clasificación en 1.9.1. Sería genial para todos los que usan productos configurables para solucionarlo.

Si alguien quiere ver eso, puede hacerlo aquí en la tienda de demostración de magento. No pude ordenar los tamaños correctamente.

Reinsch
fuente

Respuestas:

25

Nota: Se me ha señalado que esta solución no funciona para Magento 1.9.2. Para ahorrar tiempo perdido a los demás, me gustaría señalar esto en la parte superior de esta publicación. Si desarrollo mi propia solución o encuentro la solución de otra persona que funciona para 1.9.2, actualizaré esta publicación en ese momento.

Aviso: La solución presentada aquí extiende un archivo de clase de bloque en la biblioteca principal de Magento. Revisé el código fuente de Magento antes de este enfoque y determiné que no había un buen evento para observar para evitar este enfoque. Si en una versión futura de Magento se resuelve este problema de clasificación, puede deshacer estos cambios a continuación simplemente deshabilitando la extensión en su archivo XML de la aplicación / etc / modules.

Paso 1: cree la aplicación de archivo / etc / modules / FirstScribe_CatalogOptionSortFix.xml

Contenido:

<?xml version="1.0"?>
<config>
    <modules>
        <FirstScribe_CatalogOptionSortFix>
            <active>true</active>
            <codePool>local</codePool>
            <depends>
                <Mage_Catalog />
            </depends>
        </FirstScribe_CatalogOptionSortFix>
    </modules>
</config>

Nota: Para los pasos 2 y 3, cree directorios para estos archivos según sea necesario. Por ejemplo, es posible que ya tenga la aplicación de directorio / código / local , o puede que no, según las extensiones que ya haya instalado en su sitio.

Paso 2: Cree el archivo app / code / local / FirstScribe / CatalogOptionSortFix / etc / config.xml

Contenido:

<?xml version="1.0"?>
<!--
/**
 * Magento 1.9.1.0 has a bug in that the configurable options are sorted by
 * ID rather than position for the Configurable Product's front end view script.
 * This extension addresses this problem.
 *
 * @category    FirstScribe
 * @package     FirstScribe_CatalogOptionSortFix
 * @version     2014.12.15
 */
-->
<config>
    <modules>
        <FirstScribe_CatalogOptionSortFix>
            <version>1.0.0</version>
        </FirstScribe_CatalogOptionSortFix>
    </modules>
    <global>
        <blocks>
            <catalog>
                <rewrite>
                    <product_view_type_configurable>FirstScribe_CatalogOptionSortFix_Block_Product_View_Type_Configurable</product_view_type_configurable>
                </rewrite>
            </catalog>
        </blocks>
    </global>
</config>

Paso 3: Cree el archivo app / code / local / FirstScribe / CatalogOptionSortFix / Block / Product / View / Type / Configurable.php

Contenido:

<?php
/**
 * Magento 1.9.1.0 has a bug in that the configurable options are sorted by
 * ID rather than position for the Configurable Product's front end view script.
 * This extension addresses this problem.
 *
 * @category    FirstScribe
 * @package     FirstScribe_CatalogOptionSortFix
 * @version     2014.12.15
 */
class FirstScribe_CatalogOptionSortFix_Block_Product_View_Type_Configurable extends Mage_Catalog_Block_Product_View_Type_Configurable
{
    /**
     * @var Magento_Db_Adapter_Pdo_Mysql
     */
    protected $_read;

    /**
     * @var string
     */
    protected $_tbl_eav_attribute_option;

    /**
     * Composes configuration for js
     *
     * @version 2014.12.15 - Addition of this line:
     *    $info['options'] = $this->_sortOptions($info['options']);
     *
     * @return string
     */
    public function getJsonConfig()
    {
        $attributes = array();
        $options    = array();
        $store      = $this->getCurrentStore();
        $taxHelper  = Mage::helper('tax');
        $currentProduct = $this->getProduct();

        $preconfiguredFlag = $currentProduct->hasPreconfiguredValues();
        if ($preconfiguredFlag) {
            $preconfiguredValues = $currentProduct->getPreconfiguredValues();
            $defaultValues       = array();
        }

        foreach ($this->getAllowProducts() as $product) {
            $productId  = $product->getId();

            foreach ($this->getAllowAttributes() as $attribute) {
                $productAttribute   = $attribute->getProductAttribute();
                $productAttributeId = $productAttribute->getId();
                $attributeValue     = $product->getData($productAttribute->getAttributeCode());
                if (!isset($options[$productAttributeId])) {
                    $options[$productAttributeId] = array();
                }

                if (!isset($options[$productAttributeId][$attributeValue])) {
                    $options[$productAttributeId][$attributeValue] = array();
                }
                $options[$productAttributeId][$attributeValue][] = $productId;
            }
        }

        $this->_resPrices = array(
            $this->_preparePrice($currentProduct->getFinalPrice())
        );

        foreach ($this->getAllowAttributes() as $attribute) {
            $productAttribute = $attribute->getProductAttribute();
            $attributeId = $productAttribute->getId();
            $info = array(
                    'id'        => $productAttribute->getId(),
                    'code'      => $productAttribute->getAttributeCode(),
                    'label'     => $attribute->getLabel(),
                    'options'   => array()
            );

            $optionPrices = array();
            $prices = $attribute->getPrices();
            if (is_array($prices)) {
                foreach ($prices as $value) {
                    if(!$this->_validateAttributeValue($attributeId, $value, $options)) {
                        continue;
                    }
                    $currentProduct->setConfigurablePrice(
                            $this->_preparePrice($value['pricing_value'], $value['is_percent'])
                    );
                    $currentProduct->setParentId(true);
                    Mage::dispatchEvent(
                            'catalog_product_type_configurable_price',
                            array('product' => $currentProduct)
                    );
                    $configurablePrice = $currentProduct->getConfigurablePrice();

                    if (isset($options[$attributeId][$value['value_index']])) {
                        $productsIndex = $options[$attributeId][$value['value_index']];
                    } else {
                        $productsIndex = array();
                    }

                    $info['options'][] = array(
                            'id'        => $value['value_index'],
                            'label'     => $value['label'],
                            'price'     => $configurablePrice,
                            'oldPrice'  => $this->_prepareOldPrice($value['pricing_value'], $value['is_percent']),
                            'products'  => $productsIndex,
                    );
                    $optionPrices[] = $configurablePrice;
                }
            }

            // CALL SORT ORDER FIX
            $info['options'] = $this->_sortOptions($info['options']);

            /**
             * Prepare formated values for options choose
             */
            foreach ($optionPrices as $optionPrice) {
                foreach ($optionPrices as $additional) {
                    $this->_preparePrice(abs($additional-$optionPrice));
                }
            }
            if($this->_validateAttributeInfo($info)) {
                $attributes[$attributeId] = $info;
            }

            // Add attribute default value (if set)
            if ($preconfiguredFlag) {
                $configValue = $preconfiguredValues->getData('super_attribute/' . $attributeId);
                if ($configValue) {
                    $defaultValues[$attributeId] = $configValue;
                }
            }
        }

        $taxCalculation = Mage::getSingleton('tax/calculation');
        if (!$taxCalculation->getCustomer() && Mage::registry('current_customer')) {
            $taxCalculation->setCustomer(Mage::registry('current_customer'));
        }

        $_request = $taxCalculation->getDefaultRateRequest();
        $_request->setProductClassId($currentProduct->getTaxClassId());
        $defaultTax = $taxCalculation->getRate($_request);

        $_request = $taxCalculation->getRateRequest();
        $_request->setProductClassId($currentProduct->getTaxClassId());
        $currentTax = $taxCalculation->getRate($_request);

        $taxConfig = array(
                'includeTax'        => $taxHelper->priceIncludesTax(),
                'showIncludeTax'    => $taxHelper->displayPriceIncludingTax(),
                'showBothPrices'    => $taxHelper->displayBothPrices(),
                'defaultTax'        => $defaultTax,
                'currentTax'        => $currentTax,
                'inclTaxTitle'      => Mage::helper('catalog')->__('Incl. Tax')
        );

        $config = array(
                'attributes'        => $attributes,
                'template'          => str_replace('%s', '#{price}', $store->getCurrentCurrency()->getOutputFormat()),
                'basePrice'         => $this->_registerJsPrice($this->_convertPrice($currentProduct->getFinalPrice())),
                'oldPrice'          => $this->_registerJsPrice($this->_convertPrice($currentProduct->getPrice())),
                'productId'         => $currentProduct->getId(),
                'chooseText'        => Mage::helper('catalog')->__('Choose an Option...'),
                'taxConfig'         => $taxConfig
        );

        if ($preconfiguredFlag && !empty($defaultValues)) {
            $config['defaultValues'] = $defaultValues;
        }

        $config = array_merge($config, $this->_getAdditionalConfig());    

        return Mage::helper('core')->jsonEncode($config);
    }

    /**
     * Sort the options based off their position.
     *
     * @param array $options
     * @return array
     */
    protected function _sortOptions($options)
    {
        if (count($options)) {
            if (!$this->_read || !$this->_tbl_eav_attribute_option) {
                $resource = Mage::getSingleton('core/resource');

                $this->_read = $resource->getConnection('core_read');
                $this->_tbl_eav_attribute_option = $resource->getTableName('eav_attribute_option');
            }

            // Gather the option_id for all our current options
            $option_ids = array();
            foreach ($options as $option) {
                $option_ids[] = $option['id'];

                $var_name  = 'option_id_'.$option['id'];
                $$var_name = $option;
            }

            $sql    = "SELECT `option_id` FROM `{$this->_tbl_eav_attribute_option}` WHERE `option_id` IN('".implode('\',\'', $option_ids)."') ORDER BY `sort_order`";
            $result = $this->_read->fetchCol($sql);

            $options = array();
            foreach ($result as $option_id) {
                $var_name  = 'option_id_'.$option_id;
                $options[] = $$var_name;
            }
        }

        return $options;
    }
}

Paso 4: si está habilitado, actualice el tipo de caché "Configuración" de Magento en Sistema -> Administración de caché del panel de administración.

Descripción general de la extensión

  1. Extienda la clase Mage_Catalog_Block_Product_View_Type_Configurable.
  2. Agregue un método para ordenar las opciones por su positionvalor al extraer esta información de la base de datos.
  3. Vuelva a escribir el método getJsonConfig para llamar a nuestra nueva función después de haber reunido las opciones para un atributo.
Darren Felton
fuente
2
funciona fantásticamente y me alegra que la solución no afecte las actualizaciones futuras, muchas gracias a usted por una solución viable.
dawhoo
Hola @Meogi, aunque parece que su solución es perfecta para los valores de los atributos, ¿estamos seguros de que todo está en orden para los cuadros de selección de productos? Notó un problema al ordenarlos de la forma en que se establecen dentro del conjunto de atributos. Por ejemplo, teníamos "Color" arrastrado sobre "Tamaño" dentro del conjunto de atributos, sin embargo 1.9.1 cambió los dos (ignoró el orden). La única forma de arreglar eso fue editando el producto en sí y arrastrando el orden dentro de lo configurable. ¿Tal vez esto fue solo un producto malicioso que fue reordenado accidentalmente manualmente anteriormente?
Joe
1
@ Joe Si no me equivoco, arrastrar el atributo hacia arriba / abajo en el conjunto de atributos no afecta el orden en que se muestran en la página de detalles del producto frontal. En su lugar, debe ir a Catálogo -> Atributos -> Administrar atributos, encontrar su atributo y editar el valor de "Posición". Esto afectará tanto el orden en que se muestran los atributos configurables en la página del producto como la navegación por capas. El orden de las opciones configurables también se puede anular por producto, navegando a la pestaña "Productos asociados" en el administrador y arrastrando los atributos hacia arriba / abajo.
Darren Felton
1
@Meogi esto es solo para la posición del Bloque de navegación en capas, para cuando habilita "Usar en navegación en capas".
Joe
@Joe Ya veo, bueno, entonces no sé cómo cambiar la configuración predeterminada (tal vez siempre ha sido su ubicación en el conjunto de atributos, no estoy seguro). Puedo afirmar que en una instalación de Magento 1.9.1.0 todavía pude configurarlo en un orden de mi elección haciendo clic / arrastrando hacia arriba / abajo en la pestaña "Productos asociados" del producto configurable. Donde se enumeran entre el formulario de creación rápida y la cuadrícula del producto en la parte inferior.
Darren Felton
11

Solo para agregar mis dos centavos, las otras dos respuestas hicieron bien en señalarme en la dirección de la solución, pero pensé que lo atacaría en la fuente en lugar del punto de presentación del bloque.

Puede lograr el mismo resultado extendiendo el Mage_Catalog_Model_Resource_Product_Type_Configurable_Attribute_Collectionmodelo_loadPrices() método , que a pesar del nombre es donde se realizó un cambio (presumiblemente para el rendimiento) que da como resultado que los atributos se ordenen por ID en lugar de por relevancia.

El cambio parece haberse realizado para evitar anidadas foreach sentencias , pero a su vez también pierde el orden correcto. Esta solución modifica ligeramente la lógica actualizada para rastrear las opciones de atributos, luego realiza otro ciclo basado en el orden original para hacer la suma.

Aquí hay un tutorial ajustado similar a la respuesta de meogi anterior :


Paso 1: registra un nuevo módulo

Nota: si ya tiene uno, reutilice uno existente.

# 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>

Paso 2: crea la configuración del módulo

# 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>

Paso 3: agregue la extensión del modelo de recurso

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

Paso 4: borra tu caché


Como referencia , el cambio real a la clase principal en a git diffsería el siguiente (¡no edite directamente los archivos principales!):

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) {

Esto también está en GitHub si alguien lo quiere como referencia.

Editar: También he registrado esto como un error con Magento .

Robbie Averill
fuente
1
Increíble contribución mi amigo. +1 (sí, no se supone que use estos comentarios para decir gracias, pero lo mataste, así que tengo que jaja)
Darren Felton
Lo probé con magento 1.9.2: parece que no funciona desafortunadamente. Y no entiendo por qué Magento todavía no soluciona este error.
Reinsch
¿Se ha asegurado de que su módulo esté configurado correctamente? Estoy seguro de que lo saben, pero probablemente sea algo que llevaría tiempo verificar antes de lanzar un parche, ya que es una parte muy importante del sistema. Editar: También puede probar la solución directamente (y temporalmente) pero copiando el parche directamente en la clase principal (temporalmente)
Robbie Averill
1
@Reinsch Acabo de recibir un correo electrónico de alguien con respecto a la incompatibilidad con CE 1.9.2: he enviado una actualización a Actualicé mi repositorio de Github y lo probé en CE 1.9.2 con datos de muestra de Magento y ahora funciona correctamente
Robbie Averill
1
Gran trabajo @RobbieAverill, muchas gracias. Probado y confirmado trabajando en un sitio web Magento 1.9.2.1.
zigojacko
3

Esto no es realmente una solución adecuada, pero es lo que he hecho temporalmente para evitar tener que volver a 1.9.0.1 hasta que la próxima versión de Magento solucione el problema correctamente. Clasificará los valores de las opciones alfabéticamente, por supuesto, puede ordenarlos por lo que desee, pero no sé cómo acceder al orden de clasificación establecido en el backend y alfabéticamente es lo suficientemente bueno para mis propósitos.

Cambiar el archivo

/app/code/core/Mage/Catalog/Block/Product/View/Type/configurable.php

Cambiar línea 215

if($this->_validateAttributeInfo($info)) {
   $attributes[$attributeId] = $info;
}

a

usort($info['options'], function ($a,$b)
    {
        return strcmp($a['label'],$b['label']);
    }
);
if($this->_validateAttributeInfo($info)) {
   $attributes[$attributeId] = $info;
}
Steve
fuente
2
Consulte mi respuesta para obtener una respuesta que extienda adecuadamente la biblioteca principal de Magento en lugar de modificarla directamente. Aún así, felicito a Steve por esta respuesta, ya que me ayudó mucho a saber por dónde comenzar a desarrollar la solución que se me ocurrió.
Darren Felton
Excelente funcionó a las mil maravillas en Enterprise, incluso muchas gracias por salvarme el día ..
Bharath