关联字段
关联字段(Relationship Field)是 Payload 中最强大的字段之一。它能够轻松地将文档相互关联起来。


关联字段有多种用途,包括:
- 将
Product
文档添加到Order
文档中 - 允许
Order
通过placedBy
关系关联到Organization
或User
集合 - 为
Post
文档分配Category
文档
要添加关联字段,只需在字段配置中将 type
设置为 relationship
:
import type { Field } from 'payload'
export const MyRelationshipField: Field = {
// ...
// highlight-start
type: 'relationship',
relationTo: 'products',
// highlight-end
}
配置选项
选项 | 描述 |
---|---|
name * | 作为属性名用于数据库存储和检索。了解更多 |
relationTo * | 提供一个或多个 collection 的 slug ,用于建立关联关系。 |
filterOptions | 用于筛选 UI 中显示选项及验证的查询。了解更多。 |
hasMany | 布尔值,设置为 true 时允许该字段关联多个文档而非仅一个。 |
minRows | 当存在值时,验证允许的最小项目数。与 hasMany 配合使用。 |
maxRows | 当存在值时,验证允许的最大项目数。与 hasMany 配合使用。 |
maxDepth | 设置该字段的最大填充深度,无论到达该字段时的剩余深度如何。最大深度 |
label | 在 Admin Panel 中用作字段标签的文本,或为每种语言提供键值对的对象。 |
unique | 强制 Collection 中每个条目该字段的值必须唯一。 |
validate | 提供自定义验证函数,将在 Admin Panel 和后台执行。了解更多 |
index | 为该字段构建索引以加快查询速度。如果用户会频繁查询该字段数据,请设置为 true 。 |
saveToJWT | 如果该字段是顶层字段且嵌套在支持身份验证的配置中,则将其数据包含在用户 JWT 中。 |
hooks | 提供字段钩子来控制该字段的逻辑。更多详情。 |
access | 提供字段访问控制,指定用户可以查看和操作该字段数据的权限。更多详情。 |
hidden | 完全限制该字段在所有 API 中的可见性。仍会保存到数据库,但不会出现在任何 API 或 Admin Panel 中。 |
defaultValue | 提供用于该字段默认值的数据。了解更多 |
localized | 启用该字段的本地化功能。需要在基础配置中启用本地化。 |
required | 要求该字段必须有值。 |
admin | 管理员特定配置。更多详情。 |
custom | 用于添加自定义数据(如插件)的扩展点 |
typescriptSchema | 通过提供 JSON schema 来覆盖字段类型生成 |
virtual | 设置为 true 可禁用数据库中的字段,或提供字符串路径以将虚拟字段与关联关系链接。参见虚拟字段 |
graphQL | 该字段的自定义 graphQL 配置。更多详情 |
* 星号表示该属性为必填项。
提示: 深度参数可用于自动填充 API 返回的关联文档。
管理选项
要自定义 Relationship Field 在管理面板中的外观和行为,你可以使用 admin
选项:
import type { Field } from 'payload'
export const MyRelationshipField: Field = {
// ...
admin: {
// highlight-line
// ...
},
}
Relationship Field 继承了基础字段管理配置中的所有默认选项,并额外支持以下选项:
属性 | 描述 |
---|---|
isSortable | 设置为 true 可允许在管理界面中通过拖拽对此字段进行排序(仅在 hasMany 设为 true 时有效)。 |
allowCreate | 设置为 false 可禁止在关系字段内创建新文档。 |
allowEdit | 设置为 false 可禁止在关系字段内编辑文档。 |
sortOptions | 为关系字段下拉选项定义默认排序规则。了解更多 |
placeholder | 自定义文本或函数来替换默认占位符 |
appearance | 设置为 drawer 或 select 可改变字段行为。默认为 select 。 |
排序选项
你可以通过两种方式指定 sortOptions
:
作为字符串:
提供一个字符串来定义所有关系字段下拉框的全局默认排序字段。可以在字段名前加上减号("-")表示降序排序。
示例:
sortOptions: 'fieldName',
此配置将使所有关系字段下拉框按 "fieldName"
升序排序。
作为对象:
指定一个对象,其中键是集合 slugs,值是要排序的字段名字符串。这允许为每个集合的关系下拉框设置不同的排序字段。
示例:
sortOptions: {
"pages": "fieldName1",
"posts": "-fieldName2",
"categories": "fieldName3"
}
在此配置中:
- 与
pages
相关的下拉框将按"fieldName1"
升序排序。 posts
的下拉框将使用"fieldName2"
降序排序("-"前缀表示)。categories
关联的下拉框将基于"fieldName3"
升序排序。
注意:如果未定义 sortOptions
,将使用关系字段下拉框的默认排序行为。
关系选项过滤
可以通过提供查询约束来动态限制选项,该约束将用于验证输入和过滤UI中可用的关系。
filterOptions
属性可以是一个Where
查询,也可以是一个返回true
表示不过滤、false
表示阻止所有,或者返回Where
查询的函数。当使用函数时,它将接收一个包含以下属性的参数对象:
属性 | 描述 |
---|---|
blockData | 最近父块的数据。如果字段不在块内或在列表视图中的Filter 组件上调用时,将为undefined 。 |
data | 包含当前正在编辑的完整collection或global文档的对象。在列表视图中的Filter 组件上调用时,将为空对象。 |
id | 当前正在编辑文档的id 。在create 操作期间或在列表视图中的Filter 组件上调用时,将为undefined 。 |
relationTo | 要过滤的collection slug ,限于此字段的relationTo 属性。 |
req | Payload请求对象,包含对payload 、user 、locale 等的引用。 |
siblingData | 包含仅限此字段同一父级下字段的文档数据的对象。在列表视图中的Filter 组件上调用时,将为空对象。 |
user | 包含当前认证用户的对象。 |
示例
import type { CollectionConfig } from 'payload'
export const ExampleCollection: CollectionConfig = {
slug: 'example-collection',
fields: [
{
name: 'purchase',
type: 'relationship',
relationTo: ['products', 'services'],
filterOptions: ({ relationTo, siblingData }) => {
// 根据关系类型动态返回 Where 查询
if (relationTo === 'products') {
return {
stock: { greater_than: siblingData.quantity },
}
}
if (relationTo === 'services') {
return {
isAvailable: { equals: true },
}
}
},
},
],
}
你可以在这里了解更多关于编写查询的信息查询文档。
注意:
当 relationship 字段同时具有 filterOptions 和自定义的
validate 函数时,API 不会验证 filterOptions 除非你在 validate 函数中调用从
payload/shared 导入的默认 relationship 字段验证函数。
双向关系
单独的 relationship
字段用于定义包含该 relationship 字段的文档的关系,这可以被视为"单向"关系。例如,如果你有一个 Post 文档,它上面有一个 category
relationship 字段,相关的 category
本身不会显示任何关于设置了该 category 的 posts 的信息。
然而,relationship
字段可以与 Join
字段结合使用,产生强大的双向关系编辑能力。如果你对双向关系感兴趣,请查看Join 字段的文档。
数据保存方式
由于 relationship
字段类型存在多种配置选项,创建和更新这些字段所需的数据结构也会有所不同。以下部分将描述该字段可能产生的各种数据结构。
一对一关系
最简单的关联模式是使用 hasMany: false
并指定仅允许关联单一集合类型的 relationTo
。
{
slug: 'example-collection',
fields: [
{
name: 'owner', // 必填
type: 'relationship', // 必填
relationTo: 'users', // 必填
hasMany: false,
}
]
}
按此方式配置的字段,其文档保存数据结构如下:
{
// 关联用户的 ObjectID
"owner": "6031ac9e1289176380734024"
}
通过 REST API 查询该集合中的文档时,可使用如下查询方式:
?where[owner][equals]=6031ac9e1289176380734024
一对一多态关系
也称为动态引用,在此配置中,relationTo
字段是一个集合 slug 数组,用于指定 Payload 允许关联的有效集合。
{
slug: 'example-collection',
fields: [
{
name: 'owner', // 必填
type: 'relationship', // 必填
relationTo: ['users', 'organizations'], // 必填
hasMany: false,
}
]
}
关联多种类型时,文档保存的数据结构如下:
{
"owner": {
"relationTo": "organizations",
"value": "6031ac9e1289176380734024"
}
}
以下是按此数据查询文档的示例(注意引用 owner.value
的差异):
?where[owner.value][equals]=6031ac9e1289176380734024
您还可以查询字段关联特定集合的文档:
?where[owners.relationTo][equals]=organizations
此查询将仅返回拥有组织关联关系的文档。
多对多关系
hasMany
告诉 Payload 该字段可能保存了多个集合的关联关系。
{
slug: 'example-collection',
fields: [
{
name: 'owners', // 必填
type: 'relationship', // 必填
relationTo: 'users', // 必填
hasMany: true,
}
]
}
要向 hasMany
关系字段保存数据,我们需要传入一个 ID 数组:
{
"owners": ["6031ac9e1289176380734024", "602c3c327b811235943ee12b"]
}
查询文档时,数组的查询格式保持不变:
?where[owners][equals]=6031ac9e1289176380734024
多对多 - 多态关系
{
slug: 'example-collection',
fields: [
{
name: 'owners', // 必填
type: 'relationship', // 必填
relationTo: ['users', 'organizations'], // 必填
hasMany: true,
required: true,
}
]
}
当 hasMany
设置为关联多个集合时,关系字段会以对象数组的形式保存数据——每个对象包含集合 slug
作为 relationTo
值,以及关联文档的 id
作为 value
:
{
"owners": [
{
"relationTo": "users",
"value": "6031ac9e1289176380734024"
},
{
"relationTo": "organizations",
"value": "602c3c327b811235943ee12b"
}
]
}
查询方式与之前的多态关系示例相同:
?where[owners.value][equals]=6031ac9e1289176380734024
查询和过滤多态关系
由于相关数据的存储方式不同,且在不同集合之间可能存在不一致性,多态关系和非多态关系的查询方式必须有所区别。因此,在 Collection List 管理界面中,对多态关系字段的过滤仅限于 id
值。
对于多态关系,响应始终是一个对象数组。每个对象都包含 relationTo
和 value
属性。
可以通过关联文档的 ID 来查询数据:
?where[field.value][equals]=6031ac9e1289176380734024
或者通过关联文档的 Collection slug 来查询:
?where[field.relationTo][equals]=your-collection-slug
但是,你不能查询关联文档中的任何字段值。由于我们引用了多个集合,你尝试查询的字段可能不存在,从而导致查询失败。
注意:
你不能像查询非多态关系那样查询多态关系中的字段。
通过关系字段链接虚拟字段
你可以通过关系字段(或上传字段)将虚拟字段链接到其他集合中的字段,例如:
{
collections: [
{
slug: 'categories',
fields: [
{
name: 'title',
type: 'text',
},
],
},
{
slug: 'posts',
fields: [
{
type: 'relationship',
name: 'category',
relationTo: 'categories',
},
{
type: 'text',
name: 'categoryTitle',
virtual: 'category.title',
},
],
},
],
}
在这个例子中,categoryTitle
将始终填充对应的值,即使当前 depth
为 0
。你也可以通过这个字段进行查询和排序。需要注意的是,关系字段不能是 hasMany: true
或多态的。
路径可以深度嵌套到 2 个或更多关系字段中,例如 post.category.title
,只要所有关系字段都满足上述要求即可。
自定义组件
字段
服务端组件
import type React from 'react'
import { RelationshipField } from '@payloadcms/ui'
import type { RelationshipFieldServerComponent } from 'payload'
export const CustomRelationshipFieldServer: RelationshipFieldServerComponent =
({ clientField, path, schemaPath, permissions }) => {
return (
<RelationshipField
field={clientField}
path={path}
schemaPath={schemaPath}
permissions={permissions}
/>
)
}
客户端组件
'use client'
import React from 'react'
import { RelationshipField } from '@payloadcms/ui'
import type { RelationshipFieldClientComponent } from 'payload'
export const CustomRelationshipFieldClient: RelationshipFieldClientComponent = (
props,
) => {
return <RelationshipField {...props} />
}
标签
服务器组件
import React from 'react'
import { FieldLabel } from '@payloadcms/ui'
import type { RelationshipFieldLabelServerComponent } from 'payload'
export const CustomRelationshipFieldLabelServer: RelationshipFieldLabelServerComponent =
(clientField, path) => {
return (
<FieldLabel
label={clientField?.label || clientField?.name}
path={path}
required={clientField?.required}
/>
)
}
客户端组件
'use client'
import React from 'react'
import { FieldLabel } from '@payloadcms/ui'
import type { RelationshipFieldLabelClientComponent } from 'payload'
export const CustomRelationshipFieldLabelClient: RelationshipFieldLabelClientComponent =
({ field, path }) => {
return (
<FieldLabel
label={field?.label || field?.name}
path={path}
required={field?.required}
/>
)
}