引言
本设计文档面向DouPHP内容管理系统的“内容公共字段”统一规范,覆盖文章、案例、产品等所有可发布内容的通用字段设计与实现约定。重点包括:
- SEO优化字段的统一设计(keywords、description、slug)
- 内容状态管理(status)的标准定义与使用规范
- 排序机制(sort)与点击统计(click)的实现原理
- 时间字段(add_time、update_time、created_at)的统一格式与处理逻辑
- 内容模型的通用查询方法与性能优化建议 目标是给内容管理系统架构师提供一套可复用的字段设计规范与落地参考。
项目结构
DouPHP采用模块化组织,内容模型集中在 front/admin 的 model 目录中,并通过 core/model/concerns 中的通用 trait 提供统一的字段写入与格式化能力;路由与URL构建在 core/web/routing 与 config/route.php 中完成。
graph TB
subgraph "核心通用"
CM["ContentMutators<br/>字段写入器"]
URLB["UrlBuilder<br/>URL构建与slug解析"]
ROUTE["route.php<br/>短地址规则"]
end
subgraph "前台模型"
FA["Front Article"]
FC["Front Cases"]
FP["Front Product"]
end
subgraph "后台模型"
BA["Admin Article"]
end
CM --> FA
CM --> FC
CM --> FP
CM --> BA
URLB --> FA
URLB --> FC
URLB --> FP
ROUTE --> URLB
核心组件
- 内容字段写入器(ContentMutators):集中处理标题、slug、关键词、描述、排序、时间等公共字段的规范化写入,确保跨模块一致性与数据质量。
- 前台内容模型(Article/Cases/Product):通过 casts、appends、translatable、prefetchers 声明式配置字段类型、列表展示字段、多语言字段与批量预加载策略。
- 后台内容模型(如 Admin Article):通过 fillable 白名单限定持久化字段,结合 ContentMutators 完成入库前清洗。
- URL构建与路由(UrlBuilder + route.php):基于 slug 与分类别名生成短地址,支持短地址家族模式。
架构总览
内容公共字段贯穿“写入—存储—读取—渲染—路由”全链路:
- 写入阶段:后台或API调用Model::fill()时,由ContentMutators对关键字段进行清洗与类型转换。
- 存储阶段:按fillable白名单持久化到具体业务表(article/cases/product)。
- 读取阶段:前台模型通过casts将数据库值转换为所需类型(如int、attachment、datetime),appends附加派生字段,translatable按当前语言覆写显示文本。
- 渲染阶段:列表页通过prefetchers批量预热URL、附件、多语言等,减少N+1查询。
- 路由阶段:UrlBuilder根据slug与分类别名生成短地址,支持短地址家族。
sequenceDiagram
participant Admin as "后台/前端请求"
participant Model as "内容模型(Article/Cases/Product)"
participant Mutator as "ContentMutators"
participant DB as "数据库"
participant Router as "UrlBuilder"
Admin->>Model : 提交内容(含title/slug/keywords/description/sort/time)
Model->>Mutator : 字段写入器处理
Mutator-->>Model : 标准化后的字段值
Model->>DB : 写入/更新记录
Note over Model,DB : 仅允许fillable字段持久化
Admin->>Router : 访问详情(带id/slug)
Router->>DB : 根据slug/分类别名解析URL参数
Router-->>Admin : 返回目标页面
详细组件分析
SEO优化字段(keywords、description、slug)统一设计
- keywords与description
- 写入:由ContentMutators的setKeywordsAttribute/setDescriptionAttribute保证为字符串类型,避免空值污染。
- 展示:前台模型通过translatable声明这些字段随当前语言覆写,详情页SEO可直接取用。
- 列表:通过HasContentListFields等trait在列表Presenter中输出description摘要。
- slug
- 写入:setSlugAttribute对空值进行安全处理,保留有效slug。
- 路由:UrlBuilder在构建详情URL时检测并注入slug占位,配合短地址家族规则生成稳定短链。
- 兼容:若slug为空则回退为空串,避免无效URL。
flowchart TD
Start(["写入内容"]) --> KW["keywords/description 转字符串"]
Start --> SL["slug 清理空值"]
KW --> Save["持久化到业务表"]
SL --> Save
Save --> Read["前台读取<br/>translatable按语言覆写"]
Read --> Render["详情页SEO输出"]
Read --> Route["UrlBuilder生成短地址"]
内容状态管理(status)标准定义与使用规范
- 标准定义
- 已发布:status = 1(或'1'),用于前台可见内容过滤。
- 未发布/草稿:其他值(如0),用于后台管理与筛选。
- 使用规范
- 前台模型通过scopePublished统一过滤已发布内容,确保列表与详情只暴露已发布项。
- 后台模型通过casts将status映射为多语言可读串,便于界面展示。
- 模块schema声明hasStatus与publishedValue,供上层Reader按模块能力分支。
classDiagram
class Front_Article {
+scopePublished(query) Builder
+moduleSchema() array
}
class Front_Cases {
+scopePublished(query) Builder
+moduleSchema() array
}
class Front_Product {
+scopePublished(query) Builder
+moduleSchema() array
}
class Admin_Article {
+casts status -> data_lang
}
Front_Article --> "使用" Status : "where('status', 1)"
Front_Cases --> "使用" Status : "where('status', 1)"
Front_Product --> "使用" Status : "where('status', '1')"
Admin_Article --> "展示" Status : "多语言映射"
排序机制(sort)与点击统计(click)实现原理
- 排序(sort)
- 写入:ContentMutators的setSortAttribute强制为整数或空,避免非法值影响排序。
- 默认顺序:各前台模型提供scopeApplyDefaultOrder,统一为“sort ASC, id DESC”,保证手动排序优先、同序按ID倒序。
- 后台排序:受配置features.sort控制,开启时按sort升序+id降序,否则仅按id降序。
- 点击统计(click)
- 类型:前台模型cast为int,确保计数累加正确。
- 使用:详情页或列表入口处按业务需要递增,保持数值类型一致性。
flowchart TD
SStart(["保存/更新"]) --> SortCheck{"sort是否为数字?"}
SortCheck --> |是| CastInt["转为int"]
SortCheck --> |否| ClearSort["清空为''"]
CastInt --> Persist["持久化"]
ClearSort --> Persist
Persist --> ListOrder["列表默认排序<br/>sort ASC, id DESC"]
时间字段(add_time、update_time、created_at)统一格式与处理逻辑
- 统一格式
- created_at:前台模型cast为datetime:Y-m-d,列表展示统一日期格式。
- add_time/update_time:通过ContentMutators的setAddTimeAttribute将输入转为时间戳或空值,确保一致性。
- 处理逻辑
- 写入:传入null或空字符串时置为空,避免脏数据。
- 读取:前台通过HasAddTimeAccessors等trait提供add_time_short等便捷属性,用于列表快速展示。
- 归档:scopeFilterByArchive基于created_at进行年/月范围筛选,统一时间窗查询。
flowchart TD
TStart(["写入时间"]) --> CheckNull{"值为null或空?"}
CheckNull --> |是| SetEmpty["设为空"]
CheckNull --> |否| ToTS["转为时间戳"]
SetEmpty --> PersistT["持久化"]
ToTS --> PersistT
PersistT --> ReadT["前台读取<br/>cast datetime:Y-m-d"]
ReadT --> Short["accessor提供add_time_short"]
内容模型的通用查询方法与性能优化建议
- 通用查询方法
- scopePublished:仅返回已发布内容,统一状态过滤。
- scopeFilterByArchive:按创建时间范围筛选,支持年/月归档。
- scopeImageNotEmpty:仅返回有主图的内容,提升列表美观度。
- scopeApplyDefaultOrder:默认排序(sort ASC, id DESC),保证排序一致性。
- 关联查询:with('category')批量加载分类,避免N+1。
- 性能优化建议
- 使用prefetchers批量预热url、language、attachment、gallery_first等,减少重复计算与查询。
- 列表页按需选择field限制返回列,降低传输开销。
- 对高频查询字段建立索引(如category_id、status、created_at、sort、slug)。
- 详情页点击统计建议使用原子递增或异步队列,避免阻塞响应。
依赖关系分析
- 模型与Trait依赖
- 前台模型依赖HasAddTimeAccessors、HasCateInfoAccessor、HasCategoryFilter、HasContentLift、HasContentListFields、HasExportableContent、HasRelatedQuery、HasUrlAccessor等,形成稳定的读层能力集合。
- 后台模型依赖ContentMutators与HasCategoryFilter,确保写入与筛选的一致性。
- URL与路由依赖
- UrlBuilder依赖数据库字段存在性检查(如slug、category_id),并结合route.php短地址规则生成稳定链接。
graph LR
A["前台 Article"] --> T1["HasAddTimeAccessors"]
A --> T2["HasCateInfoAccessor"]
A --> T3["HasCategoryFilter"]
A --> T4["HasContentLift"]
A --> T5["HasContentListFields"]
A --> T6["HasExportableContent"]
A --> T7["HasRelatedQuery"]
A --> T8["HasUrlAccessor"]
B["前台 Cases"] --> T1
B --> T2
B --> T3
B --> T4
B --> T5
B --> T6
B --> T7
B --> T8
C["前台 Product"] --> T1
C --> T2
C --> T3
C --> T4
C --> T5
C --> T6
C --> T7
C --> T8
D["后台 Article"] --> M["ContentMutators"]
D --> T3
U["UrlBuilder"] --> R["route.php"]
性能考量
- 列表页批量预热:通过prefetchers一次性加载url、语言、附件、相册首图等,显著减少N+1查询。
- 字段裁剪:使用field限制返回列,避免冗余数据传输。
- 索引建议:对category_id、status、created_at、sort、slug建立合适索引,提升过滤与排序性能。
- 点击统计:采用原子操作或异步任务,避免同步写放大影响接口延迟。
- 缓存策略:对频繁访问的详情与列表结果引入缓存层(如Redis),降低数据库压力。
故障排查指南
- slug为空导致短地址失败
- 现象:UrlBuilder无法生成包含slug的短地址。
- 排查:确认setSlugAttribute是否正确清理空值;检查数据库中slug字段是否被意外清空。
- 解决:确保slug非空后再启用短地址;必要时回退到id路径。
- 排序异常
- 现象:列表未按sort排序或出现乱序。
- 排查:确认setSortAttribute是否将非法值清空;检查scopeApplyDefaultOrder是否被覆盖。
- 解决:统一使用默认排序;在后台开启features.sort后验证排序逻辑。
- 时间字段格式不一致
- 现象:列表显示异常或归档筛选失效。
- 排查:确认setAddTimeAttribute与casts datetime:Y-m-d是否生效;检查传入时间格式。
- 解决:统一通过写入器处理时间;列表使用accessor展示。
结论
DouPHP通过ContentMutators与各内容模型的组合,实现了内容公共字段的统一设计与高内聚实现。SEO字段、状态管理、排序与点击统计、时间字段均具备一致的写入、存储、读取与展示规范。配合prefetchers与scope系列方法,系统在可扩展性与性能方面达到良好平衡。建议在新内容模块开发中严格遵循本规范,以确保系统的一致性与可维护性。
附录
- 字段对照表(示例)
- keywords:字符串,SEO关键词,支持多语言覆写。
- description:字符串,SEO描述,支持多语言覆写。
- slug:字符串,短地址标识,参与URL构建。
- status:整型/字符串,内容状态,1表示已发布。
- sort:整型,手动排序权重,默认ASC。
- click:整型,点击计数,需原子递增。
- created_at/add_time/update_time:时间戳或datetime,统一格式化与展示。