简介
本文档面向 DouPHP 后台数据管理功能的开发者,围绕数据模型层(ORM 映射、关系定义、查询构建器)、服务层(业务封装、验证、事务、缓存)、请求验证机制(表单规则、自定义验证器、批量验证)以及常用能力(导出导入、搜索过滤、分页)进行系统化说明。文档以产品模块为例,给出可复用的模式与最佳实践,并附带安全与性能建议。
项目结构
DouPHP 后台采用分层组织:
- 模型层:位于 admin/model,继承 core/orm/Model,声明表名、字段转换、预加载、关系与方法级作用域等。
- 服务层:位于 admin/service,封装业务逻辑,协调模型、附件、日志、配置等。
- 请求层:位于 admin/request,按模块组织表单校验规则与场景化验证。
- ORM 基础:core/orm 提供 Model、Builder、Collection、Relations 等通用能力。
graph TB
subgraph "后台模块"
M["admin/model/*"] --> S["admin/service/*"]
R["admin/request/*"] --> S
C["admin/controller/*"] --> S
end
subgraph "ORM 基础"
OModel["core/orm/Model"]
OBuilder["core/orm/Builder"]
ORel["core/orm/relations/*"]
end
S --> OModel
OModel --> OBuilder
OBuilder --> ORel
核心组件
- 模型基类 Model:提供属性访问、类型转换、关联关系、事件、全局作用域、时间戳、填充与保存等能力。
- 查询构造器 Builder:在底层连接之上提供 with 预加载、全局作用域应用、水合与集合操作、分页等。
- 关系体系:BelongsTo/HasOne/HasMany 等关系实现 eager load 与匹配。
- 服务层 ProductService:聚合模型、附件、配置、日志等,完成列表、新增、更新、删除、批量操作等业务流程。
架构总览
后台数据管理的典型调用链:控制器接收请求 → 使用 Request 校验 → 调用 Service → Service 组合 Model 查询/写入 → 通过 ORM 执行 SQL → 返回结果或响应。
sequenceDiagram
participant U as "管理员"
participant Ctrl as "控制器"
participant Req as "请求校验(Request)"
participant Svc as "服务(ProductService)"
participant Mod as "模型(Product)"
participant ORM as "ORM(Builder/Model)"
participant DB as "数据库"
U->>Ctrl : 提交表单/列表请求
Ctrl->>Req : 校验参数
Req-->>Ctrl : 通过/错误
Ctrl->>Svc : 调用业务方法
Svc->>ORM : 构建查询/写入
ORM->>DB : 执行SQL
DB-->>ORM : 结果集
ORM-->>Svc : 模型/集合
Svc-->>Ctrl : 业务结果
Ctrl-->>U : 页面/JSON响应
详细组件分析
数据模型层:ORM 映射、关系与查询构建器
- 表与主键:子类声明 $table,默认主键为 id;可通过 getTable/getKeyName 获取。
- 字段转换 casts:统一将原始值转换为所需类型或格式化输出(如图片附件、日期格式、多语言状态)。
- 预加载 prefetchers:声明式批量预热,避免 N+1 问题。
- 关系定义:通过 belongsTo/hasOne/hasMany 等方法声明,配合 with() 实现高效预加载。
- 查询构建器:支持 where/order/limit/paginate 等链式调用;终结方法 get/first/find/paginate 返回水合后的模型/集合;支持全局作用域与 scope 方法。
- 事件与生命周期:支持 deleting/deleted 等事件钩子,便于清理关联资源。
classDiagram
class Model {
+string table
+string primary
+array fillable
+array casts
+array prefetchers
+array with
+query()
+create(attributes)
+destroy(id)
+getAttribute(key)
+setAttribute(key, value)
}
class Builder {
+with(relations)
+where(...)
+orderBy(field, dir)
+paginate(size, page, ...)
+get()
+first(field)
+find(id, field)
}
class Relation
class BelongsTo
class HasMany
Model <|-- Product
Model --> Builder : "newQuery()"
Builder --> Relation : "eager load"
Relation <|-- BelongsTo
Relation <|-- HasMany
关系定义与预加载
- 一对多/多对一:Product 通过 category_id 关联分类,使用 belongsTo 声明;列表页通过 with('category') 预加载分类信息,减少额外查询。
- 预加载策略:with() 支持嵌套路径与约束闭包;prefetchers 用于批量预热附件 URL 等派生字段。
- 性能要点:优先使用 with 预加载,避免循环内多次查询;对大列表使用 field() 限制字段。
查询构建器与作用域
- 作用域:scopeFilterByKeyword、scopeApplyDefaultOrder、scopePublished、scopeOwnedByWork 等,将常见条件封装为可复用查询片段。
- 全局作用域:可在 boot() 中注册全局 scope,自动应用到读路径;支持 withoutGlobalScope(s) 局部跳过。
- 聚合与标量:count/sum/value/exists 等聚合方法会应用全局作用域,保证一致性。
服务层业务封装
- 列表构建:组合 with、filter、field、order、paginate,返回模板友好的数据结构。
- 新增流程:处理内容 XSS、远程图片本地化、会员价序列化、主图上传、草稿认领、审计日志记录。
- 更新流程:校验存在性、处理内容与图片、增量保存、审计日志。
- 删除流程:二次确认、审计日志、触发模型删除事件清理关联资源。
- 批量操作:批量删除、批量改分类,结合审计日志。
flowchart TD
Start(["开始"]) --> Validate["校验参数/权限"]
Validate --> |通过| BuildList["构建列表查询<br/>with/field/order/paginate"]
Validate --> |失败| ReturnErr["返回错误"]
BuildList --> Render["渲染模板/返回JSON"]
Render --> End(["结束"])
请求验证机制
- 表单验证:每个模块的 request 类集中定义 rules() 与场景化验证(如 store/update),确保输入合法性。
- 自定义验证器:可基于框架提供的校验工具扩展规则,或在 service 中进行复杂校验。
- 批量数据验证:对数组型字段(如 checkbox[])进行类型与范围校验,再进入服务层处理。
数据导出导入、搜索过滤、分页处理
- 搜索过滤:通过 model 的作用域 filterByCategory/filterByKeyword 组合条件,保持查询可维护。
- 分页处理:使用 paginate 指定每页大小、页码与 URL 参数,返回 list 与 pager。
- 导出导入:可复用 attachment/file 存储能力与 DB 流式读取,结合队列或异步任务处理大批量数据。
依赖关系分析
- 模型依赖 ORM 基础能力(Model/Builder/Relations)。
- 服务依赖模型、附件、配置、日志、价格服务等外部能力。
- 控制器依赖服务与请求校验,不直接操作数据库。
graph LR
Controller["控制器"] --> Request["请求校验"]
Controller --> Service["服务(ProductService)"]
Service --> Model["模型(Product)"]
Model --> ORM["ORM(Model/Builder/Relations)"]
Service --> Ext["附件/配置/日志/价格服务"]
性能与大数据处理
- 预加载与字段裁剪:使用 with() 预加载关联,使用 field() 仅取必要字段,降低内存与网络开销。
- 作用域复用:将公共条件封装为 scope,避免重复 SQL 拼接。
- 批量操作:使用 destroy/inWhere/update 等批量接口,减少往返次数。
- 流式处理:对超大列表使用原生查询资源逐条处理(如缩略图重建),避免一次性加载。
- 索引与排序:合理设计索引,避免全表扫描;必要时在数据库层排序。
故障排查指南
- 主键非法传入:Builder::find/whereKey 对非标量主键有防护,抛出异常提示。
- 数据不存在:服务层在更新/删除前检查记录是否存在,不存在时抛出领域异常并返回路由。
- 权限越界:通过 scopeOwnedByWork 限定工作台数据边界,防止跨租户访问。
- 附件与多语言清理:模型删除事件触发清理图库与多语言记录,确保数据一致性。
结论
DouPHP 后台数据管理通过清晰的模型-服务-控制器分层与强大的 ORM 能力,提供了高内聚、低耦合的数据处理能力。借助作用域、预加载、批量操作与事件机制,既能满足复杂业务需求,又能保障性能与一致性。建议在新增功能时遵循现有模式,将查询条件与服务逻辑分别下沉到模型与服务层,确保可维护性与可扩展性。
附录:开发示例与最佳实践
创建新数据模型
- 新建模型类继承 Model,声明 $table、$fillable、$casts、$prefetchers。
- 如需关联,定义关系方法(belongsTo/hasOne/hasMany)。
- 将常用查询条件封装为 scope 方法,供服务层组合使用。
实现复杂业务逻辑
- 在服务层编排流程:参数校验 → 数据处理(XSS/本地化/序列化)→ 模型写入 → 附件处理 → 审计日志。
- 对敏感操作增加二次确认与权限边界控制。
处理大数据量的 CRUD
- 列表:使用 field() 限制字段、with() 预加载、paginate 分页。
- 批量:使用 destroy/inWhere/update 批量接口。
- 超大数据:使用原生查询资源流式处理(如缩略图重建),避免一次性加载。
数据导出导入
- 导出:基于 DB 查询生成 CSV/Excel,注意分批与内存控制。
- 导入:解析文件后逐条校验并写入,结合事务与错误回滚。
搜索过滤与分页
- 搜索:通过 scope 组合条件,空值透传。
- 分页:paginate 返回 list 与 pager,URL 参数规范化。
数据安全与性能优化建议
- 安全:
- 所有用户输入经 Request 校验,服务层再次校验关键参数。
- 内容入库前进行 XSS 过滤;远程图片本地化需校验来源。
- 使用作用域限制数据边界(如工作台归属)。
- 性能:
- 使用 with() 预加载关联,避免 N+1。
- 使用 field() 限制字段,减少数据传输。
- 合理使用索引与排序,避免全表扫描。
- 批量操作优先使用批量接口。
- 对超大任务采用流式处理与异步任务。