简介
本文件面向后端开发者,系统化说明 DouPHP 前台模型层的数据访问模式与 ORM 使用方式。内容涵盖:
- 数据表映射、关联关系、查询构建器用法
- 业务规则验证与数据转换(casts/accessors)
- 事件机制与钩子函数
- 批量操作、分页与预加载
- 数据缓存策略与性能优化建议
项目结构
前台模型位于 front/model,按业务域分目录组织;核心 ORM 能力由 core/orm 提供,包括 Model 基类、Builder 查询构造器、集合与关系等。前端的 Concerns 以可复用行为(如时间戳 accessor、URL 生成、分类信息注入等)增强模型。
graph TB
subgraph "前台模型"
P["Product"]
PC["ProductCategory"]
C1["HasAddTimeAccessors"]
C2["HasUrlAccessor"]
C3["HasCateInfoAccessor"]
C4["HasContentLift"]
C5["HasRelatedQuery"]
end
subgraph "核心ORM"
M["Model(基类)"]
B["Builder(查询构造器)"]
end
P --> M
PC --> M
P --> C1
P --> C2
PC --> C3
P --> C4
P --> C5
M --> B
核心组件
- 模型基类 Model:提供 ActiveRecord 风格的数据访问、属性读写、类型转换、关联关系、全局作用域、模型事件、时间戳、多语言字段等能力。
- 查询构造器 Builder:封装链式查询、预加载 with()、全局作用域应用、水合为模型集合、聚合方法透传等。
- 前台 Concerns:通过 trait 形式为模型附加通用能力(如自动填充创建时间、生成 URL、分类信息注入、内容权重、关联查询辅助等)。
架构总览
前台业务模型继承核心 ORM 的 Model,并通过 Concerns 组合能力;查询通过 Builder 完成,支持 with 预加载、全局作用域、聚合方法与原生 SQL 共存。
classDiagram
class Model {
+string table
+string primary
+array fillable
+array casts
+array prefetchers
+array with
+array appends
+array translatable
+query()
+create(attributes)
+destroy(id)
+getTable()
+getKeyName()
+newFromBuilder(attributes)
+setAttribute(key,value)
+getAttribute(key)
+boot()
+deleting(callback)
+deleted(callback)
}
class Builder {
-Model model
-Connection query
-array eagerLoad
+with(relations)
+whereKey(id)
+orderBy(field,direction)
+get()
+first(field)
+find(id,field)
+paginate(...)
+__call(method,params)
}
class Product
class ProductCategory
class HasAddTimeAccessors
class HasUrlAccessor
class HasCateInfoAccessor
class HasContentLift
class HasRelatedQuery
Product --> Model : "继承"
ProductCategory --> Model : "继承"
Product ..> HasAddTimeAccessors : "使用"
Product ..> HasUrlAccessor : "使用"
ProductCategory ..> HasCateInfoAccessor : "使用"
Product ..> HasContentLift : "使用"
Product ..> HasRelatedQuery : "使用"
Model --> Builder : "newQuery()"
详细组件分析
模型基类 Model:数据访问与生命周期
- 数据表映射:通过 $table 声明主表名;未声明时按类名蛇形推导。
- 主键与实例键:$primary 定义主键字段;getKey()/getKeyName() 暴露键名与值。
- 批量写入白名单:$fillable 控制 create/fill 的可写字段。
- 类型转换:$casts 声明字段类型转换(如 datetime、json、int 等),读取时经 CastResolver 转换,写入时反向转换。
- 默认预加载:$with 指定默认预加载的关系。
- 声明式预热:$prefetchers 用于批量预热派生字段或附件。
- 多语言字段:$translatable 与 $translatableModule 在 toArray 时按当前语言覆写。
- 属性读写:getAttribute/setAttribute 支持 accessor/mutator 与 cast;getDirty/isDirty 跟踪变更。
- 静态入口:query/create/destroy/__callStatic 统一对外 API。
- 全局作用域:addGlobalScope/getGlobalScopes/withoutGlobalScope/withoutGlobalScopes。
- 模型事件:deleting/deleted 注册监听,fireModelEvent 派发。
查询构造器 Builder:链式查询与预加载
- 连接与表:构造时基于 Model::getConnection()->table(model->getTable()) 建立底层查询。
- 全局作用域:在 get/paginate 等读路径前应用全局 scope,聚合方法也受保护。
- 预加载 with:支持单关系、多关系、嵌套关系与闭包约束,归一化为 eagerLoad。
- 终结方法:get/first/find/paginate 返回水合后的 Collection/Model;count/sum/value/exists 等聚合原样透传。
- 水合流程:hydrateRows 将原始行转为模型集合,执行 eager load 与 PrefetchRunner,再触发 afterHydrate 回调。
- 关系加载:eagerLoadRelations/loadRelation 根据 with 配置批量加载并匹配结果。
sequenceDiagram
participant U as "调用方"
participant Q as "Model : : query()"
participant B as "Builder"
participant DB as "底层Connection"
participant C as "Collection"
U->>Q : 调用 where(...)->with(...)->get()
Q-->>B : new Query 实例
B->>DB : 组装链式条件
B->>B : applyGlobalScopes()
B->>DB : select()
DB-->>B : 原始行数组
B->>B : hydrateRows()
B->>B : eagerLoadRelations()
B->>B : PrefetchRunner : : run()
B-->>U : Collection(已水合+预加载)
前台业务模型:产品与分类
- 产品模型 Product:通常包含商品基础信息、价格、库存、状态等字段;可通过 casts 进行数值/日期/JSON 转换;通过 accessors 计算展示字段(如售价、是否在售);通过 relations 关联分类、品牌、图片等。
- 产品分类模型 ProductCategory:维护分类层级与排序;常配合 HasCateInfoAccessor 提供分类信息注入;可与商品形成一对多关系。
提示:具体字段与关系请以各模型文件为准。以下为常见实践指引。
Concerns:可复用的模型行为
- HasAddTimeAccessors:为模型提供创建时间字段的 accessor 与格式化输出。
- HasUrlAccessor:为模型提供 URL 生成逻辑,便于模板渲染。
- HasCateInfoAccessor:为分类相关模型注入分类信息到查询结果。
- HasContentLift:为内容型模型提供权重/置顶等显示逻辑。
- HasRelatedQuery:提供关联查询辅助方法,简化常见关联场景。
依赖关系分析
- 模型对 ORM 的依赖:所有前台模型均依赖 core/orm/Model 提供的统一数据访问契约。
- 查询构造器对底层的依赖:Builder 依赖底层 Connection 完成 SQL 拼装与执行,同时叠加 with 预加载与模型水合。
- Concerns 对模型的增强:Concerns 以 trait 形式注入方法,不改变模型继承体系,提升内聚性。
graph LR
A["前台模型(Product/Category)"] --> B["核心ORM(Model)"]
B --> C["查询构造器(Builder)"]
C --> D["底层数据库连接(Connection)"]
A --> E["Concerns(时间/URL/分类/权重/关联)"]
性能与缓存策略
- 预加载优先:使用 with('category','brand') 避免 N+1 查询;嵌套关系可用 'category.parent' 语法。
- 声明式预热:利用 $prefetchers 批量计算派生字段(如封面图、价格区间),减少循环内重复计算。
- 只读聚合:count/sum/value/exists 等聚合方法会应用全局作用域,保证统计口径一致。
- 合理选择字段:first()/paginate() 中按需指定 field,减少不必要列传输。
- 事务与批量:大批量更新/删除尽量使用 Builder 的 update/delete 链式方法;需要触发事件的批量删除使用 destroy([ids])。
- 缓存建议:
- 热点配置与字典数据可使用系统缓存服务(非 ORM 内置)进行缓存。
- 列表页结果可按“查询条件哈希”作为缓存键,设置合理过期时间。
- 预加载结果可在内存级缓存(请求级)避免重复计算。
故障排查指南
- 主键非法传入:find/whereKey 要求标量主键,传入 null/空串/数组/对象会抛出异常,检查调用处参数类型。
- 全局作用域影响统计:聚合方法会自动应用全局作用域,若发现 count 与 get()->count() 不一致,检查是否有 scope 修改了查询条件。
- 事件中断删除:deleting 监听器返回 false 会中止删除,确认业务校验逻辑。
- 脏字段判断异常:有 cast 的字段比较采用宽松比较,确保 isDirty/getDirty 符合预期。
- 预加载失败:with 指定的关系不存在或返回非 Relation 会被忽略,检查关系方法命名与返回值。
结论
DouPHP 前台模型层以轻量级 ActiveRecord 为核心,结合 Builder 查询构造器与 Concerns 组合能力,提供了统一的 CRUD、关联、预加载、事件与类型转换机制。遵循本文的访问模式与最佳实践,可实现高内聚、低耦合且高性能的数据访问层。
附录:CRUD 与复杂查询示例
以下示例仅描述调用形态与要点,具体实现请参考对应源码位置。
-
创建记录
- 使用 Model::create([...]) 批量填充并保存,返回水合后的模型或 null。
- 参考:core/orm/Model.php:496-508
-
读取单条
- 使用 Model::find($id) 或 Model::where(...)->first()。
- 参考:core/orm/Builder.php:300-306
-
读取列表与分页
- 使用 Model::where(...)->with(['category'])->orderBy('sort','DESC')->paginate(10)。
- 参考:core/orm/Builder.php:338-346
-
更新记录
- 使用 Model::whereKey($id)->update([...]) 或直接 $model->save()。
- 参考:core/orm/Builder.php:422-447
-
删除记录
- 单条:Model::destroy($id) 触发 deleting/deleted 事件。
- 批量:Model::destroy([$id1,$id2,...]) 逐条触发事件。
- 参考:core/orm/Model.php:510-543
-
复杂查询
- 多条件:where/whereIn/order/group/having/join 等链式方法透传到底层 Connection。
- 聚合:count/sum/avg/max/min/value/exists 等。
- 参考:core/orm/Builder.php:422-447
-
预加载与嵌套关系
- with('category','brand') 或 with('category.parent')。
- 参考:core/orm/Builder.php:187-230
-
类型转换与访问器
- 在模型中声明 $casts 与 get/setXxxAttribute 方法。
- 参考:core/orm/Model.php:666-729
-
事件与钩子
- 使用 static::deleting/static::deleted 注册监听器。
- 参考:core/orm/Model.php:238-298
-
全局作用域
- 使用 addGlobalScope 注册默认过滤条件;查询时使用 withoutGlobalScope 局部跳过。
- 参考:core/orm/Model.php:300-326
- 参考:core/orm/Builder.php:94-143