文档目录
商品分类表

简介

本文面向 DouPHP 电商系统的“商品分类”模块,围绕数据表 dou_product_category 的表结构设计、层级关系与分类树构建、SEO 字段、图片与描述、排序与导航同步等特性进行系统化说明。同时给出分类缓存、批量操作优化建议,以及导入导出方案,帮助开发者快速理解并扩展该模块。

项目结构

  • 表结构定义位于系统文档 SQL 与模块备份 SQL 中,二者存在历史差异:旧版使用 cat_id 作为主键,新版统一为 id 自增主键,并通过升级脚本完成迁移。
  • 模型层提供前后端统一的分类能力:
    • 后台模型声明可写字段白名单与关联业务表。
    • 前台模型启用多语言 name 字段与“分类+内容”树构建能力。
  • 通用分类树能力通过 Trait 提供 tree() / flat() 等方法,支持进程内缓存与多语言预热。
  • URL 构建器根据分类 slug 生成友好链接,支持短地址家族策略。
graph TB
A["系统表结构.sql<br/>dou_product_category"] --> B["备份SQL product.sql<br/>dou_product_category(新结构)"]
B --> C["后台模型 ProductCategory.php<br/>fillable/recordsTable"]
C --> D["Trait HasCategoryTree.php<br/>tree()/flat()"]
D --> E["前台模型 ProductCategory.php<br/>多语言/withItems()"]
E --> F["UrlBuilder.php<br/>category_slug 路由"]

核心组件

  • 数据表 dou_product_category:承载分类基础信息、层级关系、SEO 与展示配置。
  • 后台模型:声明可写字段白名单与关联的业务记录表(product),用于权限控制与批量写入过滤。
  • 前台模型:开启 name 的多语言映射,并提供 withItems 能力以在分类树上挂载商品列表。
  • 分类树 Trait:提供 tree() 与 flat() 两种形态的分类数据结构,内置进程级缓存与多语言预热。
  • URL 构建:基于分类 slug 生成稳定友好的分类页面链接,支持短地址家族策略。

架构总览

下图展示了从数据表到模型、再到分类树与 URL 生成的整体流程。

sequenceDiagram
participant DB as "数据库"
participant Model as "分类模型"
participant Tree as "分类树 Trait"
participant Front as "前台模型(withItems)"
participant URL as "URL构建器"
Note over Model,Tree : 读取全部分类并按 sort,id 排序
Model->>DB : 查询 product_category
Tree->>DB : 获取分类行(进程缓存)
Tree-->>Model : 返回嵌套树/扁平列表
Front->>Front : 按 parent_id 递归挂 list/child
Front->>URL : 生成 category 路由
URL-->>Front : 返回分类链接

详细组件分析

数据表结构与字段语义

  • 表名:dou_product_category
  • 主键:id(自增,小整型)
  • 父级关系:parent_id(默认 0 表示顶级分类)
  • SEO 字段:slug(URL 标识)、keywords(关键词)、description(描述)
  • 展示字段:name(名称,多语言)、icon(图标路径)
  • 行为字段:sync_to_nav(是否同步至导航)、sort(排序权重)

注意:系统文档中的旧表结构使用 cat_id 作为主键;当前实际运行结构已统一为 id 自增主键,并通过升级脚本完成变更。

层级结构与分类树构建

  • 父子关系:通过 parent_id 指向父分类,形成多级树形结构。
  • 树构建:Trait 提供 tree() 方法,递归组装 child 节点,附带 level、cur、url、icon 等展示字段。
  • 扁平列表:flat() 方法输出无缩进的线性列表,便于下拉选择等场景。
  • 排序规则:按 sort ASC、id ASC 顺序加载,保证稳定的显示顺序。
  • 多语言:name/description 会在加载时进行多语言预热与回填。
flowchart TD
Start(["开始"]) --> Load["加载全部分类行<br/>按 sort,id 排序"]
Load --> BuildTree{"递归构建子树"}
BuildTree --> |parent_id=当前父级| AppendNode["附加 url/icon/cur/level/child"]
AppendNode --> NextChild["继续处理子节点"]
BuildTree --> |无匹配| Return["返回当前层级数组"]
NextChild --> BuildTree
Return --> End(["结束"])

分类路径与 URL 生成

  • slug 字段用于生成分类页面的友好 URL。
  • URL 构建器在处理 category_slug 占位时,会优先取顶级祖先的 slug(短地址家族),否则回退到当前分类 slug 或 id。
  • 这保证了无论商品属于哪一层级的分类,其分类段都能保持简洁且稳定的短地址。

SEO 优化与展示字段

  • SEO 三件套:slug(URL 友好)、keywords(搜索引擎关键词)、description(搜索引擎摘要)。
  • 展示字段:name(多语言)、icon(图标,经附件服务转为完整 URL)。
  • 这些字段在分类树构建过程中会被注入到返回结构中,供前端直接渲染。

分类排序与导航同步

  • sort 字段控制分类显示顺序,越小越靠前。
  • sync_to_nav 字段控制是否将该分类同步至站点导航。当更新分类时若标记为同步,则触发导航同步逻辑(由对应 Service 调用)。

权限控制与批量写入

  • 后台模型通过 fillable 白名单限制可批量写入的字段,避免非法字段入库。
  • recordsTable 指定了分类关联的业务记录表(product),用于统计与校验(如是否存在子分类、是否存在关联记录等)。

分类继承与“分类+内容”树

  • 前台模型启用 withItems 能力,可在分类树上挂载每个分类下的商品列表(list)与子分类(child)。
  • 该能力对分类行进行进程内静态缓存,并按 locale 维度隔离,减少重复查询。
  • 对于需要展示商品列表的场景,会收集目标分类及其全部子孙分类的 ID,批量拉取商品并分组,再切片限制数量,提升性能。
sequenceDiagram
participant V as "视图/控制器"
participant M as "前台模型"
participant T as "分类树 Trait"
participant U as "URL构建器"
V->>M : 请求分类树+内容
M->>T : 读取分类行(进程缓存)
T-->>M : 返回分类集合
M->>M : 收集目标分类及子孙ID
M->>U : 生成分类链接
U-->>M : 返回链接
M-->>V : 返回带 list/child 的分类树

分类缓存机制

  • 进程内静态缓存:分类树 Trait 在单次请求内缓存全部分类行,避免重复查询。
  • 多语言预热:在加载分类行后,立即对 name/description 进行多语言预热,减少后续多次语言转换开销。
  • 前台 withItems:按 table + locale 维度缓存分类行,进一步降低跨模块复用时的查询成本。

依赖关系分析

  • 表与模型:dou_product_category 被后台/前台两个 ProductCategory 模型引用,分别承担管理端与展示端的职责。
  • 模型与 Trait:两者均引入 HasCategoryTree,获得分类树与扁平列表能力。
  • 前台增强:前台模型额外引入 HasCategoryWithItems,实现“分类+内容”树。
  • URL 构建:分类页 URL 由 UrlBuilder 根据 slug 生成,受短地址家族策略影响。
classDiagram
class 后台ProductCategory {
+table="product_category"
+primary="id"
+recordsTable="product"
+fillable[...]
}
class 前台ProductCategory {
+translatable=["name"]
+prefetchers["language"=>"name"]
}
class HasCategoryTree {
+tree(currentId)
+flat(currentId, mark)
-categoryRows(table)
-categoryTreeWalk(...)
-categoryFlatWalk(...)
}
class HasCategoryWithItems {
+withItems(module, parentId, itemNumber, child)
-withItemsCategoryRows(table)
-withItemsCollectCatIds(...)
-withItemsBuildTree(...)
}
class UrlBuilder {
+build(category_slug,...)
}
后台ProductCategory --> HasCategoryTree : "use"
前台ProductCategory --> HasCategoryTree : "use"
前台ProductCategory --> HasCategoryWithItems : "use"
前台ProductCategory --> UrlBuilder : "生成分类URL"

性能考量

  • 分类树读取:
    • 使用进程内静态缓存,避免同一请求内重复查询。
    • 按 sort、id 排序,减少应用层排序开销。
  • 多语言预热:
    • 在加载分类行后立即预热 name/description,降低后续语言转换成本。
  • “分类+内容”树:
    • 先收集分类 ID 集合,再一次性拉取商品并分组,减少 N+1 查询。
    • 对商品列表进行切片,控制返回体量。
  • URL 构建:
    • 短地址家族策略减少 URL 层级深度,利于缓存与分享。

故障排查指南

  • 主键不一致:
    • 若发现 cat_id 与 id 混用,检查是否已完成升级脚本的字段变更。
  • 分类无法显示或顺序异常:
    • 检查 sort 字段值与 parent_id 设置是否正确。
  • 分类 URL 不正确:
    • 确认 slug 是否填写,以及 URL 构建器的短地址家族策略是否符合预期。
  • 多语言名称未生效:
    • 确认是否在分类树加载时进行了多语言预热,以及当前语言环境是否正确。

结论

dou_product_category 表采用简洁的父子关系设计,配合 Trait 提供的分类树能力与前台“分类+内容”树构建,实现了高效、可扩展的商品分类体系。通过 slug、keywords、description 等字段满足 SEO 需求,sort 与 sync_to_nav 支撑展示与导航控制。结合进程级缓存与多语言预热,整体性能良好。建议在大规模数据场景下继续使用批量查询与切片策略,并严格遵循字段白名单与升级脚本,确保数据一致性与稳定性。

附录

表结构参考(当前运行结构)

  • 表名:dou_product_category
  • 字段:
    • id:主键,自增
    • slug:URL 标识
    • name:分类名称(多语言)
    • icon:分类图标
    • keywords:SEO 关键词
    • description:SEO 描述
    • parent_id:父分类 ID
    • sync_to_nav:是否同步至导航
    • sort:排序权重

导入导出与批量操作建议

  • 导入:
    • 建议使用批量插入(事务包裹),按 sort、parent_id 顺序导入,避免循环单条插入。
    • 导入前清理或校验 slug 唯一性,防止 URL 冲突。
  • 导出:
    • 导出包含 slug、name、keywords、description、parent_id、sort、sync_to_nav 等关键字段,便于二次编辑与迁移。
  • 批量操作:
    • 利用后台模型的 fillable 白名单进行安全批量写入。
    • 对涉及导航同步的操作,批量更新后再统一触发一次导航同步,减少重复计算。
添加日期:2026-10-05