将Magento属性按小数排序而不是按字母数字顺序排序

12

我已经疯狂地在谷歌上搜索,试图找到一个真正有效的解决方案来解决这个问题,但是都没有结果。

在分类页面使用“按函数排序”功能按属性(容量、重量等)对产品进行排序时,Magento会以文本字符串的形式进行排序,因为它认为数字是文本字符串:

产品A: 10kg

产品B: 11kg

产品C: 15kg

产品D: 9kg

而实际上应该这样排序:

产品D: 9kg

产品A: 10kg

产品B: 11kg

产品C: 15kg

查看了一些资料后,人们建议将想要按数字排序的属性在eav_attribute表中的backend_type更改为小数并将frontend_input更改为价格。然而,这不仅似乎无法真正起作用,而且它会改变数字的格式,在其前面加上美元符号($),因为我们在产品页面上显示实际属性值,并且还要通过它进行排序,所以它并不能解决问题。

我正在尝试弄清楚getSortOrder()方法的确切作用,但是看起来这种功能被嵌入得非常深,所以我很难想出解决这个错误的方法。

任何帮助都将不胜感激!

编辑:

对于未来想要解决此问题的任何人,这是我想出的解决方法:

您需要覆盖存储在app/Code/Mage/core/catalog/block/product/list.php中的List.php中的_getProductCollection()函数。
将该文件复制到app/code/Mage/local/catalog/block/product/list.php中,以避免编辑核心文件。

然后,在下面它说的位置:

$this->_productCollection = $layer->getProductCollection();

请放置以下代码:

        // Start of Code to force Magento to numerically sort decimal attributes rather than alphabetically

        $filterAttribute = $this->getRequest()->getParam('order');
        $filterAttributeDir = $this->getRequest()->getParam('dir');
        $attributeType = Mage::getModel('eav/entity_attribute')->loadByCode('catalog_product', $filterAttribute)->getFrontendClass();

        // If a Sort By option is selected on category page and attribute has frontend_class = validate-number or validate-digits 
        // then CAST the attribute values as signed integers

        if (isset($filterAttribute) && ($attributeType == 'validate-digits' || $attributeType == 'validate-number')) {

            $this->_productCollection->getSelect()->reset(Zend_Db_Select::ORDER);
            $this->_productCollection->getSelect()->order('CAST(`' . $filterAttribute . '` AS SIGNED) ' . $filterAttributeDir . "'");

        }

        // End of code to force Magento to numerically sort....

如果您在管理员面板中为属性设置为十进制数或整数数的商店所有者进行输入验证,则此代码将重置产品集合上的排序顺序,然后将其转换为有符号整数,以使其按数值而不是按字母表顺序排序。

希望对某些人有所帮助!


你能提供一下你正在使用的类和方法来将排序顺序设置为该属性吗? - Anthony
太好了,它能正常工作。这解决了我面临的大麻烦。非常感谢。 - Akash Pius
这在从前端工具栏过滤器中选择选项时有效,但如果默认排序顺序是通过过滤器进行的,则会像以前一样行为。有什么想法吗? - Ricky Odin Matthews
6个回答

14

所以我在他们的文档中找到了关于这个问题的帖子,显然这是该产品的一个已知痛点。我发现最好的解决方案是通过调用Collection类的原始方法来覆盖查询的ORDER BY,以下是他们给出的示例:

$_productCollection = Mage::getModel('catalog/product')->getCollection();

$_productCollection->setOrder(array('cm_brand', 'name', 'cm_length'), 'asc');
$_productCollection->getSelect()->reset(Zend_Db_Select::ORDER);
$_productCollection->getSelect()->order(array('cm_brand ASC', 'name ASC', 'CAST(`cm_length` AS SIGNED) ASC'));

根据您提供的只有一个排序列的示例,我认为您可以使用以下方法:

$_productCollection = Mage::getModel('catalog/product')->getCollection();
$_productCollection->setOrder('weight', 'asc');
$_productCollection->getSelect()->reset(Zend_Db_Select::ORDER);
$_productCollection->getSelect()->order('CAST(`weight` AS SIGNED) ASC'));

嘿,很抱歉这里回复晚了 - 我刚刚才有时间再次查看。我在此处打了一个绿色的勾号,因为这确实可以重新对产品集合进行排序。现在我的唯一问题是尝试将其转化为更适用于我的需求的通用方法。在catalog/block/product/list.php中的_getProductCollection中,我可以使用您提供的代码覆盖$this->_productCollection,但理想情况下,我现在正在尝试找到一种方法来查看产品集合的当前属性排序方式,查看它是否在后端类型中列为十进制,并继续进行CAST。 - Adam B
1
你刚刚救了我一命,先生! - marcinsdance

3

回应Adam B的帖子。

我的情况是:我需要按数字值对自定义Magento(1.8.1.0)属性的产品进行排序。然而,Magento的排序方式是:

1 , 11 , 123 ,2 ,234, 3(字母数字混合), 而不是:1, 2, 3, 11, 123, 234(仅数字)

老实说,我不明白为什么这个功能不能直接使用,但是我通过以下调整使其正常工作:

$this->_productCollection->getSelect()->order(new Zend_Db_Expr("CAST( ".$filterAttribute." AS SIGNED ) ".$filterAttributeDir));

希望对某些人有所帮助。


0

对于默认排序顺序:

        // Start of Code to force Magento to numerically sort decimal attributes rather than alphabetically
        $filterAttribute = Mage::getBlockSingleton('catalog/product_list_toolbar')->getCurrentOrder();
        //$filterAttribute = $this->getRequest()->getParam('order');
        $filterAttributeDir = Mage::getBlockSingleton('catalog/product_list_toolbar')->getCurrentDirection();
        //$filterAttributeDir = $this->getRequest()->getParam('dir');

0

您能告诉我们 'Catalog Input Type for Store Owner' 是什么吗?例如,它是文本还是下拉框?我的意思是,在Magento管理后台(目录->属性->管理属性->管理标签/选项->管理选项(您的属性值))中是否有“位置”列。如果您的属性是文本,则实际值是字符串'9kg'还是后来添加了'kg'的'9'?

无论如何,如果您可以在属性编辑器中设置位置,那么我认为getSortOrder()将对您有所帮助(确认一下,有人可以发布关于getSortOrder()的答案)。

如果您无法设置位置(因为值未预先确定),则我认为您需要自己创建产品排序顺序*,可能使用preg_replace()str_replace()从属性值中去除字母,根据结果进行排序,然后将新排序的产品数组传递到显示循环中-请参见此答案中的“粗略伪代码”

*因为我认为任何内置的通用排序函数都无法告诉字符串'9kg'小于字符串'11kg'或者'3V'小于'18V'

**编辑以下评论:

啊,是的,我看到Magento没有Catalog Input Type for Store Owner的数字输入类型。我认为你应该在.phtml或相关的块php类中编写自己的排序代码(或者如果您预先知道权重,请将其设置为下拉菜单,例如1,2,3,4...或1.1,1.2,1.3...4.1,4.2,4.3...19.9),然后您可以告诉Magento位置。

您可能会使用Date输入类型甚至Fixed product tax输入类型,但我认为这是在寻找麻烦(和大量测试)。

计划B可能是添加一些XML,定义Catalog Input Type for Store Owner的新数字输入类型,但我会把它留给核心Magento开发人员-也许他们有一个将属性值存储为字符串的好理由。

如果您正在使用系统属性Weight,那么它被固定为输入类型text,我认为您别无选择,只能编写自己的排序代码。
我刚刚发现
//file: app/code/core/Mage/Core/Model/Locale.php
//class: Mage_Core_Model_Locale
//...
     /*
     * @param string|float|int $value
     * @return float|null
     */
    public function getNumber($value){
//...

这可能会很有用。我认为编写自己的排序算法不会花费你太长时间。


该值存储为9,而不是9kg,对于造成的困惑我们感到抱歉。如果您按字母数字排序,“9”将在“19”之后,这使得Magento感到困惑,但它需要进行数字排序。 - Adam B

0

0

抱歉,我有点晚加入讨论。

由于我不喜欢在没有必要的情况下实现代码解决方案,因此我试图想出一个更简单的解决方案。我想到了以下方法。

示例问题排序 原始排序顺序:10.0"、10.5"、14.0"、8.0"、8.5"

考虑到数字列表是按字母数字顺序排序的,我推断在我的示例中在 8 前添加偏移空格字符(" ")应该会得到正确的排序结果。确实如此。这就是结果。

示例正确排序 新的排序顺序:8.0"、8.5"、10.0"、10.5"、14.0"

在 OP 中,Adam 可能只需将 "9kg" 替换为 " 9kg"。

扩展一下,如果所涉及的数字范围从个位数到百位数,那么个位数将有 2 个前导空格,十位数将有 1 个前导空格,以此类推。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接