
本文详解如何通过重写 category 类的 getproducts() 方法,在 prestashop 1.7 分类列表页中注入自定义 sql 查询逻辑,以支持按扩展字段(如 priority、state)精准排序与筛选产品。
在 PrestaShop 1.7 中,分类页面(Category Controller)展示的产品数据并非由单一 SQL 模板驱动,而是由 Category 类的 getProducts() 方法动态构建并执行两个核心查询:
- 计数查询(COUNT):用于分页总数计算;
- 数据查询(SELECT):获取实际展示的产品列表(含关联的图片、价格、属性等)。
该方法位于 classes/Category.php,是修改产品检索逻辑的唯一官方推荐入口。直接修改核心文件不可取,应通过覆盖(Override)机制安全扩展。
✅ 正确做法:创建 Category 类覆盖文件
在 override/classes/Category.php 中创建如下覆盖类:
CWMS 2.0功能介绍:一、 员工考勤系统,国内首创CWMS2.0的企业员工在线考勤系统。二、 自定义URL Rewrite重写,友好的搜索引擎 URL优化。三、 代码与模板分离技术,支持超过5种类型的模板类型。包括:文章、图文、产品、单页、留言板。四、 购物车功能,CWMS2.0集成国内主流支付接口。如:淘宝、易趣、快钱等。完全可媲美专业网上商城系统。五、 多语言自动切换 中英文的说明。六、
0 的产品)
$where = ' AND p.`state` = 1 AND p.`priority` > 0 ';
// 2️⃣ 调用父类逻辑,但注入自定义条件
$sql = 'SELECT p.*, product_shop.*, stock.out_of_stock, IFNULL(stock.quantity, 0) as quantity'
. ', pl.`description`, pl.`description_short`, pl.`link_rewrite`, pl.`meta_description`,'
. ' pl.`meta_keywords`, pl.`meta_title`, pl.`name`, image_shop.`id_image`, il.`legend`,'
. ' m.`name` AS manufacturer_name, cl.`name` AS category_default, DATEDIFF(product_shop.`date_add`, DATE_SUB(NOW(), INTERVAL '.(Validate::isUnsignedInt(Configuration::get('PS_NB_DAYS_NEW_PRODUCT')) ? Configuration::get('PS_NB_DAYS_NEW_PRODUCT') : 20).' DAY)) > 0 AS new'
. ', product_shop.price AS orderprice'
. ' FROM `'._DB_PREFIX_.'category_product` cp'
. ' LEFT JOIN `'._DB_PREFIX_.'product` p ON p.`id_product` = cp.`id_product`'
. ' '.Shop::addSqlAssociation('product', 'p').'
. ' LEFT JOIN `'._DB_PREFIX_.'product_lang` pl ON (p.`id_product` = pl.`id_product` AND pl.`id_lang` = '.(int) $idLang.Shop::addSqlRestrictionOnLang('pl').')'
. ' LEFT JOIN `'._DB_PREFIX_.'category_lang` cl ON (cl.`id_category` = cp.`id_category` AND cl.`id_lang` = '.(int) $idLang.Shop::addSqlRestrictionOnLang('cl').')'
. ' LEFT JOIN `'._DB_PREFIX_.'image_shop` image_shop ON (image_shop.`id_product` = p.`id_product` AND image_shop.cover = 1 AND image_shop.id_shop='.(int) Context::getContext()->shop->id.')'
. ' LEFT JOIN `'._DB_PREFIX_.'image_lang` il ON (image_shop.`id_image` = il.`id_image` AND il.`id_lang` = '.(int) $idLang.')'
. ' LEFT JOIN `'._DB_PREFIX_.'manufacturer` m ON (p.`id_manufacturer` = m.`id_manufacturer`)'
. ' '.StockAvailable::addSqlAssociation('stock', 'p', false, Context::getContext()->shop->id).'
. ' WHERE cp.`id_category` = '.(int) $this->id
. $where // ? 注入自定义条件
. Shop::addSqlRestriction(Shop::SHARE_PRODUCT)
. ($active ? ' AND product_shop.`active` = 1' : '')
. ($front ? ' AND product_shop.`visibility` IN ("both", "catalog")' : '');
// 3️⃣ 自定义 ORDER BY(例如:先按 priority 降序,再按 date_add 降序)
if (!$orderBy) {
$orderBy = 'p.`priority`';
$orderWay = 'DESC';
}
// 4️⃣ 执行查询(复用 PrestaShop 分页/缓存逻辑)
if ($getTotal) {
return (int) Db::getInstance(_PS_USE_SQL_SLAVE_)->getValue(
'SELECT COUNT(*) FROM ('.$sql.') c'
);
}
$result = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS(
$sql
. ' ORDER BY '.($orderBy == 'id_product' ? 'p.`id_product`' : $orderBy).' '.$orderWay
. ' LIMIT '.(int) $n.' OFFSET '.(int) (($p - 1) * $n)
);
// 5️⃣ 补充处理(如添加图片 URL、价格格式化等),可调用 Product::getProductsProperties()
return Product::getProductsProperties($idLang, $result);
}
}⚠️ 关键注意事项
- 必须清除缓存:覆盖生效前,删除 var/cache/ 下所有内容(或后台「高级参数 → 性能」中点击「清除缓存」);
- 避免硬编码表前缀:始终使用 _DB_PREFIX_ 常量;
- 区分主从库:读操作使用 _PS_USE_SQL_SLAVE_,写操作禁用;
- 保持分页兼容性:$getTotal 分支必须返回整型计数,否则分页将异常;
- 不建议修改 COUNT 查询结构:若需复杂过滤,务必同步更新计数子查询中的 WHERE 条件,否则页码显示错误;
- 优先使用 Hook 替代硬覆盖:对于简单排序/过滤,可考虑 actionProductSearchProviderQueryBuilder 钩子(PS 1.7.7+),更轻量且升级友好。
✅ 总结
通过安全覆盖 Category::getProducts(),你完全掌控了分类页的数据源。无论是按自定义字段 priority 加权排序、按 state 状态筛选,还是关联其他业务表,均可在此方法中灵活实现。记住:覆盖是手段,可维护性与兼容性才是目标——始终遵循 PS 覆盖规范,测试分页、缓存与多语言场景,确保升级无忧。









