如何在 PrestaShop 1.7 中自定义分类页的产品查询 SQL

14次阅读

如何在 PrestaShop 1.7 中自定义分类页的产品查询 SQL

本文详解如何通过重写 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 中创建如下覆盖类:

 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 覆盖规范,测试分页、缓存与多语言场景,确保升级无忧。

text=ZqhQzanResources