0%

EspoCRM定制第3篇: 纯配置多对多——不写 SQL,让 rebuild 自动建表

适用版本:EspoCRM 9.2.2+

你以为多对多必须建中间表?在 EspoCRM 里,手写 SQL 往往是你自己给自己埋雷。

TL;DR

  • entityDefs.links + relationName 决定关系与中间表
  • scopes 决定实体能不能被管理与配置
  • clientDefs + layouts 决定用户界面”看不看得到、用不用得起来”
  • rebuild 是让系统把元数据变成真实结构的开关

1. 场景:什么时候需要多对多

很多 CRM 场景都需要多对多关系:

  • 一个记录关联多个标签、多个分类
  • 一个项目关联多个联系人、多个资源
  • 一个工单关联多个参与人、多个附件

很多人第一反应是:建中间表、写迁移 SQL。这在 EspoCRM 里往往是最差的选择——你会绕过系统的元数据机制,升级时痛苦指数爆炸。

我们推荐的路线:只写配置rebuild → EspoCRM 自动创建表结构。

2. 目标:用一套最小配置打通”能看见、能选择、能保存”

多对多不是”数据能存进去”就完事了。真正能交付给业务用,至少要满足:

  • 关系在实体定义里存在(后端能存)
  • 关系在界面上可见(前端能选)
  • 权限与作用域可控(ACL 不出事)
  • rebuild 后自动生成/更新结构(可升级)

3. 关键机制:relationName 决定中间表

你可以把 EspoCRM 的自动建表理解为:
entityDefs 里声明了关系 → rebuild 生成实际表结构

flowchart TD
  A[entityDefs: fields + links] --> B[rebuild]
  B --> C[(主表自动创建/更新)]
  B --> D[(中间表按 relationName 自动创建)]
  B --> E[前端元数据缓存更新]

在 links 中给同一段关系设置同一个 relationName,就是告诉系统:

  • 这是同一个关联(正反两侧一致)
  • 中间表应按这个名字生成(内部映射)

4. 配置最小闭环(示例:EntityA ↔ EntityB)

用中性实体名演示:

  • CItem:条目
  • CTag:标签

4.1 定义实体(entityDefs)

1
2
3
4
5
6
7
8
9
10
11
12
13
{
"fields": {
"name": { "type": "varchar", "required": true }
},
"links": {
"tags": {
"type": "hasMany",
"entity": "CTag",
"foreign": "items",
"relationName": "cItemTag"
}
}
}

另一侧:

1
2
3
4
5
6
7
8
9
10
11
12
13
{
"fields": {
"name": { "type": "varchar", "required": true }
},
"links": {
"items": {
"type": "hasMany",
"entity": "CItem",
"foreign": "tags",
"relationName": "cItemTag"
}
}
}

4.2 定义作用域(scopes)

如果 scopes 不完整,菜单不显示、权限无法配置、布局无法编辑。

1
2
3
4
5
6
7
8
9
10
11
12
13
{
"entity": true,
"layouts": true,
"tab": true,
"acl": true,
"customizable": true,
"importable": true,
"stream": true,
"disabled": false,
"type": "Base",
"module": "Custom",
"isCustom": true
}

4.3 让关系在 UI 可见(clientDefs + layouts)

只写 entityDefs,用户会说”加了关系,但页面上看不到”。需要两层:

  • clientDefs:关系面板声明(relationshipPanels)
  • layouts:字段/面板落位(edit/detail)
1
2
3
4
5
{
"relationships": {
"tags": { "layout": null }
}
}

布局示例:

1
2
3
4
5
6
7
8
9
10
11
[
{
"rows": [
[
{ "name": "name" },
{ "name": "tags" }
]
],
"label": "Overview"
}
]

5. rebuild 之后会发生什么

  • 系统为 cItemTag 自动生成中间表
  • 两侧实体的关联可在 UI 面板中维护
  • ACL 生效:不同角色看到的范围可控

6. 部署与验证

1
2
3
4
5
6
7
# 逐文件复制
CONTAINER_NAME="<your-espocrm-container>"
docker cp CItem.json "$CONTAINER_NAME":/var/www/html/custom/Espo/Modules/MyModule/Resources/metadata/entityDefs/CItem.json
docker cp CTag.json "$CONTAINER_NAME":/var/www/html/custom/Espo/Modules/MyModule/Resources/metadata/entityDefs/CTag.json

# rebuild
docker exec "$CONTAINER_NAME" php /var/www/html/command.php rebuild

验证清单:

  • 后台能看到两个实体的菜单入口
  • 编辑页面能选择关联记录
  • 保存后关系面板能看到关联列表
  • 不同角色下 ACL 表现符合预期

7. 常见坑

  • scopes 配置缺失导致”菜单不显示/权限不可配”
  • layouts 没放字段导致”后端有关系,前端看不到”
  • 忘记 rebuild 导致”改了配置不生效”