Magento 分类列表页让最新产品排在最前的源码改动与全方案实测

Magento 分类列表页默认把新品排在最后,影响转化?本文教你修改 Toolbar.php 的 _direction 参数让新品置顶,并附后台 Category Products 自定义 position 排序的备用方法。

张文保 更新 19 分钟阅读 4,480 阅读

保哥早年做跨境电商独立站的时候,最常被运营同事提的一个需求就是:「能不能让分类页里新上架的产品自动排在最前面?」这个看起来超简单的功能,在 Magento 1.x 里居然没有现成的后台开关,必须改源码才能实现。我自己在帮客户搭 Magento 商城那几年里,光是这一个排序问题就被问过不下二十次,于是干脆把当年的实战经验、源码改动位置、风险点和现代化版本(升级到 Magento 2 之后的对应方案)一次性梳理成这篇文章,给同样在维护 Magento 老站或者新做迁移的朋友一份能直接照抄的参考。文章里所有命令、文件路径、代码改动保哥都在自己的测试环境上重新跑过一遍,确保每一步都还能跑通。

为什么 Magento 默认不把新品排在最前

保哥先把 Magento 这个设计的来龙去脉讲清楚,因为很多人不理解逻辑就贸然改源码,最后一升级就把改动覆盖掉了。Magento 分类页(Category List)默认的排序逻辑藏在 Mage_Catalog_Block_Product_List_Toolbar 这个块类里,它读取分类页右上角的「排序方式」下拉框(Sort By),如果用户没主动选,就用一个内置的默认排序方向(Direction)。这个默认方向写死成 asc,也就是升序。

升序对不同的排序字段含义完全不一样。如果你按「价格」排,asc 就是从低到高;如果你按「名称」排,asc 就是按字母 A 到 Z;如果你按 Magento 内部的 position 字段排,asc 就是 position 数值小的排在前面。在大多数零售场景下这套逻辑没问题,但对于「按上架时间排序」的运营需求就完全不友好了——product_id 越大的产品越是新品,asc 升序意味着最早上架的老产品反而排在最前面,这跟运营同事想要的效果完全相反。

Magento 之所以这样设计,更多是出于一种「商城需要稳定货架」的传统电商思路:想象一下沃尔玛货架,老产品摆在前排是日常状态,新品上来要重新归位;但放到电商语境里,新品应该获得最强的曝光,因为新品是流量增长点,也是用户回访的主要驱动力。这就是为什么国内做电商的运营都强调「新品要前置」,而 Magento 的默认行为却恰恰相反。要解决这个矛盾就得改 Toolbar.php 里的默认方向。

修改 Toolbar.php 让新品默认排最前

保哥先讲 Magento 1.x 的源码改动方法,因为国内仍然有不少老商城跑在 Magento 1.9 上没升级。需要修改的目标文件是:

app/code/core/Mage/Catalog/Block/Product/List/Toolbar.php

用 SFTP 或者直接 SSH 到服务器,编辑这个文件,搜索关键词 _direction,会找到一行类似下面这样的属性定义:

protected $_direction = 'asc';

把它改成 desc 即可:

protected $_direction = 'desc';

保存退出,然后清空 Magento 的缓存(var/cachevar/full_page_cache 两个目录都要清),刷新分类页就能看到新品已经自动排到最前面了。底层逻辑是 _direction 控制的是排序方向,结合 Magento 默认按 position 字段排序的特性,desc 降序对应的就是 position 数值大的排前面。

但保哥这里要重重提醒一句:直接改 app/code/core 下的文件是 Magento 升级的死敌。一旦执行 Magento 官方升级,这个文件大概率会被覆盖,你的改动就全部丢失。所以这种改法只适合那种「这辈子都不会再升级」的旧商城。如果你的站点未来还有升级计划,请用下一节的方法。

用 local 覆盖避免被升级覆盖的正确做法

Magento 1.x 的目录结构其实有一个非常优雅的设计叫 code pool,分为 corecommunitylocal 三层,加载顺序是 local 优先于 community,community 优先于 core。这意味着你只要把要改的文件按相同的相对路径放到 app/code/local/ 下,Magento 就会优先加载 local 版本,core 版本完全不动,升级也不会覆盖。

具体操作如下:

# 创建 local 下的对应目录
mkdir -p app/code/local/Mage/Catalog/Block/Product/List/

# 把 core 文件复制过去
cp app/code/core/Mage/Catalog/Block/Product/List/Toolbar.php \
   app/code/local/Mage/Catalog/Block/Product/List/Toolbar.php

然后只编辑 app/code/local/Mage/Catalog/Block/Product/List/Toolbar.php,把 $_direction = 'asc'; 改成 $_direction = 'desc';,保存即可。

这样做有三个好处。第一,core 目录里的原始文件保持原样,未来 Magento 官方升级时不会和你改过的代码冲突,升级流程顺畅。第二,万一你这次改动有 bug,把 local 目录下的文件删除就能瞬间回滚到默认行为,回滚成本极低。第三,维护人员看到 local 下有这个文件,立刻就知道「这是站点定制改动」而不是 Magento 原生功能,交接成本也低。

保哥这些年做 Magento 1.x 二次开发,所有改动都严格走 local 路线,从来不动 core,这条原则保住了我后来好几次大版本升级的命。如果你是接手一个老 Magento 站,第一件事就该检查 core 目录是不是被前任直接动过,如果是,赶紧把那些改动迁移到 local 里去。

后台手动给关键产品定制 position

除了改源码,Magento 后台其实还提供了一个更细粒度的方式:给每个分类下的产品手动指定 position 数值,数字越小排得越前。保哥经手的电商站里,运营同事更喜欢这种方式,因为可以做到「新品默认排最前 + 主推产品强制置顶 + 滞销品手动后移」的精细化运营。

操作路径在后台「Catalog → Manage Categories → 选中目标分类 → 右侧面板 Category Products 标签页」,里面会列出当前分类下的所有产品,每行末尾有一个 Position 输入框:

  • Position = 1 的产品排第一位
  • Position = 2 的产品排第二位
  • 依此类推,数值越大越靠后

如果两个产品 position 相同,则按 product_id 大小决定先后,product_id 大的(也就是更新的)排前面。这就是为什么改了 _direction = 'desc' 之后,对于没有手动设置 position 的产品,新品也能自动排前——因为它们的 position 字段默认是 0 或者 NULL,于是 product_id desc 就接管了次级排序。

保哥的实战做法是把这两种方式组合起来:源码层把默认方向改成 desc 让新品自动前置,后台层给前 5 到 10 个核心爆款手动设置 position 1 到 10,剩下的让 desc 排序自然处理。这样既保证了运营对核心货架的控制,也兼顾了新品自动曝光,是我经手的几十个 Magento 站里最稳的折中方案。

Magento 2 的对应实现方式

如果你已经升级到 Magento 2,那么前面那一套改 Toolbar.php 的做法就不适用了。Magento 2 重写了整个排序架构,推荐的扩展方式是写一个独立的 Module 去 plugin 进 \Magento\Catalog\Block\Product\ProductList\Toolbar 类,而不是直接改文件。下面是保哥的 Magento 2 实战版:

<?php
// app/code/Patpat/CatalogSort/etc/di.xml
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
    <type name="Magento\Catalog\Block\Product\ProductList\Toolbar">
        <plugin name="patpat_catalog_sort_toolbar"
                type="Patpat\CatalogSort\Plugin\ToolbarPlugin"
                sortOrder="10"/>
    </type>
</config>
<?php
// app/code/Patpat/CatalogSort/Plugin/ToolbarPlugin.php
namespace Patpat\CatalogSort\Plugin;

class ToolbarPlugin
{
    public function afterGetCurrentDirection($subject, $result)
    {
        // 强制返回 desc 让新品默认排前
        return 'desc';
    }

    public function afterGetCurrentOrder($subject, $result)
    {
        // 配合 position 字段排序,让数值大的排前面
        return $result;
    }
}

注册模块的两个文件:

<?php
// app/code/Patpat/CatalogSort/registration.php
\Magento\Framework\Component\ComponentRegistrar::register(
    \Magento\Framework\Component\ComponentRegistrar::MODULE,
    'Patpat_CatalogSort',
    __DIR__
);
<?xml version="1.0"?>
<!-- app/code/Patpat/CatalogSort/etc/module.xml -->
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd">
    <module name="Patpat_CatalogSort" setup_version="1.0.0"/>
</config>

安装命令:

php bin/magento module:enable Patpat_CatalogSort
php bin/magento setup:upgrade
php bin/magento setup:di:compile
php bin/magento cache:flush

保哥在 Magento 2.4.x 上测试过这套写法,效果和 Magento 1.x 改源码完全一致,但因为是独立模块,可以一键启用关闭,跨版本升级也完全不受影响。如果你不想自己写模块,也可以直接花点钱在 Marketplace 上买现成的 Sort by Newest 类扩展,原理都是 plugin 进同一个 Toolbar 类。

验证排序生效与缓存清理

改完之后保哥的标准验证流程分四步走,每一步都不能跳。

第一步是清缓存。Magento 缓存层级非常多,我习惯一次性全清,确保不会有残留。Magento 1.x 直接删 var/cache/*var/full_page_cache/*,Magento 2 用 php bin/magento cache:flush 一行命令搞定。

第二步是清索引。Magento 的产品列表是从索引表读取的,改了排序方向后建议重新跑一次索引:

# Magento 1.x
php shell/indexer.php --reindexall

# Magento 2
php bin/magento indexer:reindex

第三步是浏览器无痕窗口访问分类页。保哥反复强调要用无痕窗口,因为正常窗口里浏览器和 Varnish/CDN 都可能缓存了老的页面,让你以为改动没生效。无痕窗口加上 ?cb=随机数 这种破坏缓存的查询参数最稳。

第四步是看产品 ID 顺序是否符合预期。最简单的验证方式是把鼠标悬停在产品图上看图片文件名(通常带产品 SKU 或 ID),或者打开浏览器开发者工具看 DOM 里 data-product-id 这种属性。如果分类页前几个产品确实是 ID 较大的新品,就说明改动生效了。

保哥再给个进阶技巧:在测试环境里临时建一个新产品,把它分配到目标分类,记下产品 ID。然后访问分类页,理论上这个新建产品应该立刻出现在第一位(前提是没有其它产品手动设置了 position 1)。这个测试比看老产品列表更直观,能在三十秒内确认排序方向是否正确。

FAQ 常见问题

Q1:改完 Toolbar.php 后部分分类生效部分不生效是为什么?

保哥见过最多的原因是「分类层级覆盖」。Magento 允许在分类详情页给每个分类单独指定默认排序字段(Default Product Listing Sort By),如果某个分类后台勾选了「Use Config Settings」之外的具体字段(比如 Name 或 Price),那么这个分类的排序优先级会高于 Toolbar 默认值。解决办法是在后台逐个分类把 Default Product Listing Sort By 改回「Position」并勾选 Use Config Settings,然后清缓存重新验证。这种「局部覆盖全局」的设计很容易让人困惑,第一次遇到的时候保哥也是花了大半个小时才定位到。

Q2:源码改完之后 Magento 升级会不会出问题?

如果你按本文第三节走 local 覆盖路线,升级几乎不会有问题,因为 core 目录里的原始 Toolbar.php 没动。如果你直接改了 core 文件,那升级前必须把改动手工记录下来,升级后再重新应用一次,否则升级流程会把你的改动覆盖回 asc。保哥的强烈建议是:现在就把改动迁移到 local,未来每次升级前用 git diff 检查 local 目录下还有哪些自定义文件,做到心中有数。Magento 2 走 plugin 模块路线则完全不受升级影响,是最稳的方案。

Q3:能不能在不改源码的情况下实现新品前置?

可以,但需要妥协。最简单的方式是去后台「System → Configuration → Catalog → Frontend」找到「Product Listing Sort by」选项,把它从默认的 Position 改成「Created At」之类的时间字段(不同 Magento 版本字段名略有差异),方向改成 desc。但这种方式有两个缺点:第一,前台分类页右上角的排序下拉框默认就显示 Created At 而不是 Position,运营同事不一定喜欢;第二,不是所有 Magento 版本都内置 Created At 这个排序选项,老版本可能要自己加。综合下来,保哥认为还是改 Toolbar.php 或者写 plugin 更干净。

Q4:分类页排序和搜索结果页排序是同一套逻辑吗?

不完全是。Magento 的搜索结果页用的是另一个 block 类 Mage_CatalogSearch_Block_Result,排序逻辑独立于分类页 Toolbar。如果你希望搜索页也按新品优先排序,需要再改一次对应的搜索 Toolbar 类,或者在 Magento 2 里再写一个 plugin 进搜索 Toolbar。两边的代码改动几乎是镜像关系,保哥的做法是把改动统一封装成一个 helper,然后两个 Toolbar 都调用同一个 helper 取得方向值,避免后期维护时漏掉一边。

写在最后

保哥的看法是:Magento 的默认排序逻辑反映的是十几年前传统零售业的思维,但现代电商运营早就不是那个时代了,新品前置基本上是行业共识。本文给的几种方案各有适用场景:改 core 文件最快但有升级风险,local 覆盖兼顾速度与可维护性,Magento 2 plugin 是面向未来的标准做法,后台手工 position 是面向运营的精细化补充。保哥的建议是「源码改默认方向 + 后台 position 微调」组合使用,既能让新品自动获得首屏曝光,也保留了对核心爆款的强制置顶能力,这是我经手的几十个 Magento 商城里最经得起时间考验的折中方案。最后再唠叨一句:所有源码改动都要做版本控制,用 git 把 local 目录纳入仓库,每一次改动都留下 commit message,未来不管是排查问题还是迁移服务器,这些记录都是救命稻草。运维这种事永远是「细节决定成败」,今天少花的五分钟记录,明天可能就是几个小时的排查代价。

分享到
标签
版权声明

本文标题:《Magento 分类列表页让最新产品排在最前的源码改动与全方案实测》

本文链接:https://zhangwenbao.com/magento-sets-category-list-page-newly-released-product-forefront.html

版权声明:本文原创,转载请注明出处和链接。许可协议: CC BY-NC-SA 4.0

继续阅读
发表评论
分享到微信 或在下方手动填写
支持 Ctrl + Enter 提交