
本文详解如何通过重写 category 类的 getproducts() 方法,在 prestashop 1.7 分类列表页中注入自定义 sql 查询逻辑,以支持按扩展字段(如 priority、state)精准排序与筛选产品。
在 PrestaShop 1.7 中,分类页面(Category Controller)展示的产品数据并非由单一 SQL 模板驱动,而是由 Category 类的 getProducts() 方法动态构建并执行两个核心查询:
该方法位于 classes/Category.php,是修改产品检索逻辑的唯一官方推荐入口。直接修改核心文件不可取,应通过覆盖(Override)机制安全扩展。
✅ 正确做法:创建 Category 类覆盖文件
在 override/classes/Category.php 中创建如下覆盖类:
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 覆盖规范,测试分页、缓存与多语言场景,确保升级无忧。