# 模型关系定义

<cite>
**本文引用的文件**
- [Model.php](file://vendor/topthink/think-orm/src/Model.php)
- [RelationShip.php](file://vendor/topthink/think-orm/src/model/concern/RelationShip.php)
- [HasOne.php](file://vendor/topthink/think-orm/src/model/relation/HasOne.php)
- [HasMany.php](file://vendor/topthink/think-orm/src/model/relation/HasMany.php)
- [BelongsTo.php](file://vendor/topthink/think-orm/src/model/relation/BelongsTo.php)
- [BelongsToMany.php](file://vendor/topthink/think-orm/src/model/relation/BelongsToMany.php)
- [HasManyThrough.php](file://vendor/topthink/think-orm/src/model/relation/HasManyThrough.php)
- [HasOneThrough.php](file://vendor/topthink/think-orm/src/model/relation/HasOneThrough.php)
- [MorphOne.php](file://vendor/topthink/think-orm/src/model/relation/MorphOne.php)
- [MorphMany.php](file://vendor/topthink/think-orm/src/model/relation/MorphMany.php)
- [MorphTo.php](file://vendor/topthink/think-orm/src/model/relation/MorphTo.php)
- [MorphToMany.php](file://vendor/topthink/think-orm/src/model/relation/MorphToMany.php)
- [composer.json](file://composer.json)
- [AGENTS.MD](file://AGENTS.MD)
</cite>

## 目录
1. [简介](#简介)
2. [项目结构](#项目结构)
3. [核心组件](#核心组件)
4. [架构总览](#架构总览)
5. [详细组件分析](#详细组件分析)
6. [依赖分析](#依赖分析)
7. [性能考量](#性能考量)
8. [故障排查指南](#故障排查指南)
9. [结论](#结论)
10. [附录](#附录)

## 简介
本文件面向 ThinkCMF 项目中模型关系的设计与使用，系统性阐述 ThinkPHP 8.x ORM 在 ThinkCMF 中的关系定义与查询机制。内容覆盖一对一(hasOne)、一对多(hasMany)、多对一(belongsTo)、多对多(belongsToMany)，以及远程关联(hasOneThrough、hasManyThrough)、多态关联(morphOne、morphMany、morphTo、morphToMany、morphByMany)等。文档同时解释关系方法的定义语法（关联表、关联字段、中间表配置）、关系查询（预加载 load 与延迟加载）、关系维护（创建、更新、删除），并给出复杂关系场景的解决方案与最佳实践。

## 项目结构
- ThinkCMF 基于 ThinkPHP 8.x，核心 ORM 位于 vendor/topthink/think-orm。
- 模型关系能力通过 Model 抽象类与 RelationShip trait 提供，具体关系类型由 relation 命名空间下的各类关系类实现。
- 项目通过 composer.json 声明 PSR-4 自动加载，将 app、api、plugins、themes 等目录纳入命名空间扫描。

```mermaid
graph TB
A["Model 抽象类<br/>vendor/topthink/think-orm/src/Model.php"] --> B["RelationShip 特征<br/>vendor/topthink/think-orm/src/model/concern/RelationShip.php"]
B --> C1["HasOne<br/>HasOne.php"]
B --> C2["HasMany<br/>HasMany.php"]
B --> C3["BelongsTo<br/>BelongsTo.php"]
B --> C4["BelongsToMany<br/>BelongsToMany.php"]
B --> C5["HasOneThrough<br/>HasOneThrough.php"]
B --> C6["HasManyThrough<br/>HasManyThrough.php"]
B --> C7["MorphOne<br/>MorphOne.php"]
B --> C8["MorphMany<br/>MorphMany.php"]
B --> C9["MorphTo<br/>MorphTo.php"]
B --> C10["MorphToMany / MorphByMany<br/>MorphToMany.php"]
```

图表来源
- [Model.php](file://vendor/topthink/think-orm/src/Model.php)
- [RelationShip.php](file://vendor/topthink/think-orm/src/model/concern/RelationShip.php)

章节来源
- [composer.json](file://composer.json)
- [Model.php](file://vendor/topthink/think-orm/src/Model.php)
- [RelationShip.php](file://vendor/topthink/think-orm/src/model/concern/RelationShip.php)

## 核心组件
- Model 抽象类：提供模型生命周期、事件钩子、数据写入/更新/删除、缓存、字段白名单、只读字段、强制写入等通用能力；关系能力通过 RelationShip trait 注入。
- RelationShip 特征：集中定义关系方法（hasOne、belongsTo、hasMany、hasManyThrough、hasOneThrough、belongsToMany、morph* 系列），并提供预加载、延迟加载、关联统计、绑定属性、关联写入/删除等机制。
- 关系类：HasOne、HasMany、BelongsTo、BelongsToMany、HasOneThrough、HasManyThrough、MorphOne、MorphMany、MorphTo、MorphToMany、MorphByMany 等，封装具体查询与数据装载逻辑。

章节来源
- [Model.php](file://vendor/topthink/think-orm/src/Model.php)
- [RelationShip.php](file://vendor/topthink/think-orm/src/model/concern/RelationShip.php)

## 架构总览
下图展示 Model 与 RelationShip 的关系，以及关系方法如何返回具体关系类实例：

```mermaid
classDiagram
class Model {
+save(data, where)
+delete()
+refresh()
+__get(name)
+__set(name, value)
}
class RelationShip {
+hasOne(model, foreignKey, localKey)
+belongsTo(model, foreignKey, localKey)
+hasMany(model, foreignKey, localKey)
+hasManyThrough(model, through, foreignKey, throughKey, localKey, throughPk)
+hasOneThrough(model, through, foreignKey, throughKey, localKey, throughPk)
+belongsToMany(model, middle, foreignKey, localKey)
+morphOne(model, morph, type)
+morphMany(model, morph, type)
+morphTo(morph, alias)
+morphToMany(model, middle, morph, localKey)
+morphByMany(model, middle, morph, foreignKey)
+eagerly(query, relation, field, joinType, closure, first)
+eagerlyResultSet(resultSet, relations, withRelationAttr, join, cache)
+eagerlyResult(result, relations, withRelationAttr, join, cache)
+relationQuery(relations, withRelationAttr)
+relationCount(query, relations, aggregate, field, useSubQuery)
+bindAttr(relation, attrs)
+together(relations)
}
class HasOne
class HasMany
class BelongsTo
class BelongsToMany
class HasOneThrough
class HasManyThrough
class MorphOne
class MorphMany
class MorphTo
class MorphToMany
Model ..> RelationShip : "use"
RelationShip --> HasOne : "返回"
RelationShip --> HasMany : "返回"
RelationShip --> BelongsTo : "返回"
RelationShip --> BelongsToMany : "返回"
RelationShip --> HasOneThrough : "返回"
RelationShip --> HasManyThrough : "返回"
RelationShip --> MorphOne : "返回"
RelationShip --> MorphMany : "返回"
RelationShip --> MorphTo : "返回"
RelationShip --> MorphToMany : "返回"
```

图表来源
- [Model.php](file://vendor/topthink/think-orm/src/Model.php)
- [RelationShip.php](file://vendor/topthink/think-orm/src/model/concern/RelationShip.php)
- [HasOne.php](file://vendor/topthink/think-orm/src/model/relation/HasOne.php)
- [HasMany.php](file://vendor/topthink/think-orm/src/model/relation/HasMany.php)
- [BelongsTo.php](file://vendor/topthink/think-orm/src/model/relation/BelongsTo.php)
- [BelongsToMany.php](file://vendor/topthink/think-orm/src/model/relation/BelongsToMany.php)
- [HasOneThrough.php](file://vendor/topthink/think-orm/src/model/relation/HasOneThrough.php)
- [HasManyThrough.php](file://vendor/topthink/think-orm/src/model/relation/HasManyThrough.php)
- [MorphOne.php](file://vendor/topthink/think-orm/src/model/relation/MorphOne.php)
- [MorphMany.php](file://vendor/topthink/think-orm/src/model/relation/MorphMany.php)
- [MorphTo.php](file://vendor/topthink/think-orm/src/model/relation/MorphTo.php)
- [MorphToMany.php](file://vendor/topthink/think-orm/src/model/relation/MorphToMany.php)

## 详细组件分析

### 关系方法定义语法与默认规则
- hasOne(model, foreignKey = '', localKey = '')
  - 定义当前模型到目标模型的一对一关系
  - foreignKey 默认使用“目标模型名_id”，localKey 默认使用当前模型主键
- belongsTo(model, foreignKey = '', localKey = '')
  - 定义多对一关系，即“从属”于另一个模型
  - foreignKey 默认使用“当前模型名_id”，localKey 默认使用目标模型主键
- hasMany(model, foreignKey = '', localKey = '')
  - 定义一对多关系
  - foreignKey 默认使用“目标模型名_id”，localKey 默认使用当前模型主键
- belongsToMany(model, middle = '', foreignKey = '', localKey = '')
  - 定义多对多关系，支持指定中间表/模型
  - middle 默认为“当前模型名_目标模型名”
  - foreignKey 默认为“目标模型名_id”，localKey 默认为“当前模型名_id”
- hasOneThrough(model, through, ...)
  - 定义“远程一对一”关系，通过中间模型穿透
- hasManyThrough(model, through, ...)
  - 定义“远程一对多”关系，通过中间模型穿透
- morphOne/morphMany/morphTo/morphToMany/morphByMany
  - 多态关系族，支持同一关系指向多种不同模型类型

章节来源
- [RelationShip.php](file://vendor/topthink/think-orm/src/model/concern/RelationShip.php)
- [HasOne.php](file://vendor/topthink/think-orm/src/model/relation/HasOne.php)
- [HasMany.php](file://vendor/topthink/think-orm/src/model/relation/HasMany.php)
- [BelongsTo.php](file://vendor/topthink/think-orm/src/model/relation/BelongsTo.php)
- [BelongsToMany.php](file://vendor/topthink/think-orm/src/model/relation/BelongsToMany.php)
- [HasOneThrough.php](file://vendor/topthink/think-orm/src/model/relation/HasOneThrough.php)
- [HasManyThrough.php](file://vendor/topthink/think-orm/src/model/relation/HasManyThrough.php)
- [MorphOne.php](file://vendor/topthink/think-orm/src/model/relation/MorphOne.php)
- [MorphMany.php](file://vendor/topthink/think-orm/src/model/relation/MorphMany.php)
- [MorphTo.php](file://vendor/topthink/think-orm/src/model/relation/MorphTo.php)
- [MorphToMany.php](file://vendor/topthink/think-orm/src/model/relation/MorphToMany.php)

### 关系查询：预加载与延迟加载
- 延迟加载
  - 通过在模型上定义同名关系方法（如 user、posts），在访问属性时按需查询并缓存结果
  - RelationShip::getRelationData 会调用关系方法并获取关系数据
- 预加载（JOIN 方式）
  - eagerly：针对一对一关系进行 JOIN 预加载
  - eagerlyResultSet/eagerlyResult：对结果集或单个模型进行预加载，支持子关系与闭包过滤
- 关联条件查询
  - has、hasNot、hasWhere、hasWhereOr：基于关联存在性与条件进行筛选
- 关联统计
  - relationCount：对关联进行聚合统计（如 count、sum 等）

```mermaid
sequenceDiagram
participant M as "模型实例"
participant RS as "RelationShip"
participant Q as "查询对象"
participant R as "关系类"
M->>RS : "eagerlyResultSet(resultSet, relations, withRelationAttr, join, cache)"
RS->>RS : "解析 relations 与子关系"
RS->>M : "$relation()"
M-->>RS : "返回关系类实例 R"
RS->>R : "eagerlyResultSet(...)"
R-->>RS : "装载关联数据"
RS-->>M : "写入关联属性"
M-->>Q : "返回带关联的数据集"
```

图表来源
- [RelationShip.php](file://vendor/topthink/think-orm/src/model/concern/RelationShip.php)

章节来源
- [RelationShip.php](file://vendor/topthink/think-orm/src/model/concern/RelationShip.php)

### 关系维护：创建、更新、删除
- 关联写入/更新
  - together：声明保存主模型时同时写入/更新指定关联
  - relationSave：遍历 together 列表，对关联执行 save 或通过关系方法保存并回填主键
- 关联删除
  - relationDelete：当删除主模型时，对声明的关联执行删除
- 字段白名单与只读
  - allowField/readonly：控制可写字段与只读字段，避免意外更新
- 强制写入
  - force：强制写入或删除，跳过某些优化判断

```mermaid
flowchart TD
Start(["开始"]) --> Save["调用 save(data, where)"]
Save --> Validate["校验与过滤数据"]
Validate --> IsUpdate{"是否更新?"}
IsUpdate --> |否| Insert["执行插入并设置主键"]
IsUpdate --> |是| Update["执行更新"]
Insert --> AfterWrite["触发写入后事件"]
Update --> AfterWrite
AfterWrite --> Together{"是否声明 together?"}
Together --> |是| RelationSave["relationSave 执行关联写入/更新"]
Together --> |否| End(["结束"])
RelationSave --> End
```

图表来源
- [Model.php](file://vendor/topthink/think-orm/src/Model.php)
- [RelationShip.php](file://vendor/topthink/think-orm/src/model/concern/RelationShip.php)

章节来源
- [Model.php](file://vendor/topthink/think-orm/src/Model.php)
- [RelationShip.php](file://vendor/topthink/think-orm/src/model/concern/RelationShip.php)

### 复杂关系场景
- 多级关联
  - 使用 hasOneThrough/hasManyThrough 通过中间模型穿透，实现跨表关联
- 条件关联
  - relationQuery 支持传入闭包对关联进行条件过滤
  - hasWhere/hasWhereOr 支持基于关联字段条件筛选
- 动态关联
  - parseRelationModel 支持动态解析模型类名（含命名空间）
  - parseMorph 支持多态字段名解析（类型+外键）
- 绑定属性
  - bindAttr 将关联对象的属性绑定到当前模型，便于直接访问

章节来源
- [RelationShip.php](file://vendor/topthink/think-orm/src/model/concern/RelationShip.php)

## 依赖分析
- Model 依赖 RelationShip trait 提供关系能力
- RelationShip 依赖各关系类（HasOne、HasMany、BelongsTo、BelongsToMany、HasOneThrough、HasManyThrough、Morph*）实现具体关系
- composer.json 声明 PSR-4 自动加载，确保模型与关系类可被正确加载

```mermaid
graph LR
Model["Model 抽象类"] --> RS["RelationShip 特征"]
RS --> HO["HasOne"]
RS --> HM["HasMany"]
RS --> BT["BelongsTo"]
RS --> BM["BelongsToMany"]
RS --> HOS["HasOneThrough"]
RS --> HMS["HasManyThrough"]
RS --> MO["MorphOne"]
RS --> MM["MorphMany"]
RS --> MT["MorphTo"]
RS --> MTM["MorphToMany / MorphByMany"]
```

图表来源
- [Model.php](file://vendor/topthink/think-orm/src/Model.php)
- [RelationShip.php](file://vendor/topthink/think-orm/src/model/concern/RelationShip.php)

章节来源
- [composer.json](file://composer.json)
- [Model.php](file://vendor/topthink/think-orm/src/Model.php)
- [RelationShip.php](file://vendor/topthink/think-orm/src/model/concern/RelationShip.php)

## 性能考量
- 预加载优先：在批量查询时尽量使用预加载（eagerlyResultSet/eagerlyResult），避免 N+1 查询
- 条件限制：合理使用 hasWhere/hasWhereOr 与 relationQuery 的闭包过滤，缩小关联数据规模
- 关联统计：使用 relationCount 并结合子查询或直接聚合，减少不必要的数据装载
- 字段裁剪：明确 withAttr 与 select 字段，避免无用字段传输
- 多态与中间表：多对多与多态关系涉及额外 JOIN，应谨慎使用并建立必要索引

## 故障排查指南
- 关联方法未生效
  - 确认已定义与属性同名的关系方法，并返回相应关系类实例
  - 检查 foreignKey/localKey 配置是否与数据库一致
- 预加载无效
  - 确认 eagerlyResultSet/eagerlyResult 的 relations 参数格式正确，子关系使用“.”分隔
  - 检查 join 参数与缓存配置是否影响结果
- 关联写入失败
  - 检查 together 配置与关联对象是否正确
  - 确认 allowField/readonly 设置未阻止关键字段写入
- 多态关系异常
  - 检查多态类型字段与外键字段命名是否符合 parseMorph 的默认规则
- 远程关联查询慢
  - 为中间表与关联字段添加索引，必要时改用 hasWhere 限定范围

章节来源
- [RelationShip.php](file://vendor/topthink/think-orm/src/model/concern/RelationShip.php)
- [Model.php](file://vendor/topthink/think-orm/src/Model.php)

## 结论
ThinkCMF 基于 ThinkPHP 8.x ORM 的关系体系提供了完善的一对一、一对多、多对一、多对多及远程、多态关系能力。通过 RelationShip trait 将关系定义、查询、维护与统计统一化，配合 Model 的生命周期与事件机制，能够满足复杂业务场景的需求。建议在设计阶段明确外键与中间表命名规范，并在查询阶段优先采用预加载与条件过滤，以获得更优的性能与可维护性。

## 附录
- 数据库命名规范参考
  - 表命名：小写下划线，统一前缀 cmf_，非核心应用表加应用前缀，插件表加插件前缀
  - 通用字段：id、create_time、update_time、delete_time、status、list_order、remark、description
  - 布尔字段：以 is_/has_/exist_/can_ 开头
  - 文件相关字段：image、thumbnail、photos
  - 用户相关字段：除 user 表外，其他表使用 user_id
  - 扩展字段：more（text/longtext）
  - 层级关系字段：parent_id

章节来源
- [AGENTS.MD](file://AGENTS.MD)