Skip to content

关联字段

关联字段用于使RelationshipUpload字段在相反方向上可用。通过关联字段,您可以编辑和查看引用特定集合文档的集合。该字段本身作为虚拟字段存在,即不会在包含关联字段的集合中存储新数据。相反,管理员 UI 会显示相关文档,以提供更好的编辑体验,并通过 Payload 的 API 进行显示。

关联字段在以下场景中非常有用:

  • 显示某个产品的订单
  • 查看和编辑属于某个类别的帖子
  • 处理任何双向关系数据
  • 显示文档或上传文件在其他文档中的使用情况

Shows Join field in the Payload Admin Panel

关联字段的管理员面板截图

为了使关联字段正常工作,您必须在您要关联的集合中已有一个关系或上传字段。这将引用相关文档字段的集合和路径。要添加关系字段,请在字段配置中将类型设置为 "join":

javascript
import type { Field } from 'payload'

export const MyJoinField: Field = {
  name: 'relatedPosts',
  type: 'join',
  collection: 'posts',
  on: 'category',
}

// relationship field in another collection:
export const MyRelationshipField: Field = {
  name: 'category',
  type: 'relationship',
  relationTo: 'categories',
}

在这个示例中,字段被定义为在添加到类别集合时显示相关的帖子。on 属性用于指定与集合文档相关的字段的关系字段名称。

通过这个示例,如果您在管理员 UI 或 API 响应中导航到某个类别,您将看到与该类别相关的帖子已经为您填充。这非常强大,可以轻松地定义各种关系类型。

NOTE

关联字段具有极高的性能,在您的 API 响应中不会增加额外的查询开销,直到您增加深度为 1 或更高为止。它适用于所有数据库适配器。在 MongoDB 中,我们使用聚合自动连接相关文档,而在关系型数据库中,我们使用连接操作。

CAUTION

关联字段在 DocumentDB 和 Azure Cosmos DB 中不受支持,因为我们在这些数据库中内部使用 MongoDB 聚合查询数据,而这些数据库对聚合的支持有限。未来可能会做出调整。

架构建议

在建模数据库时,您可能会遇到很多需要建立双向关系的情况。但这里有一个重要的考虑——通常您只希望在一个地方存储关于某个关系的信息。

以帖子和类别为例。在编辑帖子时,定义帖子属于哪个类别是合理的。

但通常没有必要在类别上直接存储帖子的 ID 列表,原因如下:

  • 您希望拥有一个“单一的事实来源”来管理关系,而不是担心保持两个来源的同步。
  • 如果您有成百上千甚至数百万篇帖子,您不希望将所有这些帖子 ID 存储在一个类别中。
  • 等等。

这就是关联字段特别强大的地方。通过它,您只需在帖子中存储 category_id,Payload 会在查询类别时自动为您连接相关的帖子。相关的类别仅存储在帖子本身上,而不会在两端重复存储。然而,正是关联字段让您能够实现双向的 API 和 UI。

使用关联字段来完全控制您的数据库架构

对于典型的多态关系或多对多关系,如果您使用的是 Postgres 或 SQLite,Payload 会自动创建一个 posts_rels 表,作为连接表来存储给定文档的所有关系。

然而,如果您希望对数据库架构有更多控制,可能这不适合您的用例。您可能不希望有那个 _rels 表,而是更倾向于维护和控制自己的连接表设计。

NOTE

使用关联字段,您可以控制自己的连接表设计,避免 Payload 自动创建 _rels 表。

关联字段可以与任何集合一起使用——如果您希望定义自己的“连接”集合,例如名为 categories_posts,并具有 post_idcategory_id 列,您可以完全控制该连接表的形状。

您还可以进一步利用 categories_posts 集合的 admin.hidden 属性,将该集合隐藏,避免在管理员 UI 导航中显示。

在关系中指定附加字段

关联字段的另一个非常强大的用例是能够在关系上定义“上下文”字段。假设您有帖子(Posts)和类别(Categories),并且在帖子和类别集合上都使用了关联字段,以便从一个新的伪连接集合 categories_posts 中连接相关文档。现在,关系存储在这个第三个连接集合中,并可以在帖子和类别中展示。但重要的是,您可以向这个共享连接集合中添加额外的“上下文”字段。

例如,在 categories_posts 集合中,除了有类别和帖子字段外,我们还可以添加自定义的“上下文”字段,比如 featuredspotlight,这将允许您直接在关系上存储附加信息。关联字段为您提供了完全的控制能力,可以实现 Payload 中的任何类型的关系架构,所有这些都通过强大的管理员 UI 来实现。

配置选项

选项描述
name *用作从数据库检索时的属性名称。更多
collection *拥有关系字段的集合 slug,或者集合 slug 的数组。
on *关系或上传字段的名称,用于关联到集合文档。对于嵌套路径使用点表示法,如 'myGroup.relationName'。如果 collection 是数组,则此字段必须在所有指定集合中存在。
where用于隐藏相关文档的查询。将与请求中指定的任何 where 合并。
maxDepth默认为 1,设置此字段的最大填充深度,无论到达此字段时剩余深度是多少。最大深度。
label在管理员面板中用作字段标签的文本,或者是包含每种语言的键值对象。
hooks提供字段钩子以控制此字段的逻辑。更多细节。
access提供字段访问控制,表示哪些用户可以查看和操作此字段的数据。更多细节。
defaultLimit要返回的文档数量。设置为 0 以返回所有相关文档。
defaultSort用于指定连接文档返回顺序的字段名称。
admin管理员特定的配置。更多细节。
custom用于添加自定义数据的扩展点(例如,插件)。
typescriptSchema提供 JSON 模式以覆盖字段类型生成。
graphQL字段的自定义 GraphQL 配置。更多细节。

带星号的选项表示此属性为必填项。

管理员配置选项

您可以使用管理员配置属性来控制关联字段的用户体验。以下选项是支持的:

选项描述
defaultColumns字段名称的数组,用于指定在关系表中显示哪些列。默认是集合配置。
allowCreate设置为 false 以移除此字段创建新相关文档的控制。
components.Label覆盖字段组件的默认标签。更多细节。

关联字段数据

当返回的文档中,关联字段被填充了相关文档时,返回的结构是一个对象,包含以下内容:

  • docs:一个包含相关文档的数组,或者如果达到深度限制,则仅包含 ID。
  • hasNextPage:一个布尔值,表示是否还有更多文档。
  • totalDocs:文档的总数,仅当在关联查询中传递 count: true 时存在。
json
{
  "id": "66e3431a3f23e684075aae9c",
  "relatedPosts": {
    "docs": [
      {
        "id": "66e3431a3f23e684075aaeb9",
        // other fields...
        "category": "66e3431a3f23e684075aae9c"
      }
      // { ... }
    ],
    "hasNextPage": false,
    "totalDocs": 10 // if count: true is passed
  }
  // other fields...
}

关联字段数据(多态)

当返回的文档中,多态关联字段(集合为数组)被填充了相关文档时,返回的结构是一个对象,包含以下内容:

  • docs:一个包含 relationTo(文档的集合 slug)和 value(文档本身或在达到深度限制时为 ID)的数组。
  • hasNextPage:一个布尔值,表示是否还有更多文档。
  • totalDocs:文档的总数,仅当在关联查询中传递 count: true 时存在。
json
{
  "id": "66e3431a3f23e684075aae9c",
  "relatedPosts": {
    "docs": [
      {
        "relationTo": "posts",
        "value": {
          "id": "66e3431a3f23e684075aaeb9",
          // other fields...
          "category": "66e3431a3f23e684075aae9c"
        }
      }
      // { ... }
    ],
    "hasNextPage": false,
    "totalDocs": 10 // if count: true is passed
  }
  // other fields...
}

查询选项

关联字段支持自定义查询,以过滤、排序和限制返回的相关文档。除了每个关联字段的特定查询选项外,您还可以传递 joins: false 来禁用所有关联字段的返回。这对于在不需要相关文档时,出于性能考虑非常有用。

以下查询选项是支持的:

属性描述
limit返回的最大相关文档数量,默认为 10。
where一个可选的 Where 查询,用于过滤连接的文档。将与字段的 where 对象合并。
sort一个用于排序相关结果的字符串。
count是否包括相关文档的计数。默认不包括。

这些选项可以应用于本地 API、GraphQL 和 REST API。

本地 API

通过向本地 API 添加关联字段,您可以按字段名称自定义每个关联字段的请求。

javascript
const result = await payload.find({
  collection: 'categories',
  where: {
    title: {
      equals: 'My Category',
    },
  },
  joins: {
    relatedPosts: {
      limit: 5,
      where: {
        title: {
          equals: 'My Post',
        },
      },
      sort: 'title',
    },
  },
})

WARNING

目前,关联字段的 Where 查询对包含多个集合的关联文档的支持是有限的,并且不支持数组和块内部的字段。

WARNING

目前,无法通过关联字段本身进行查询,这意味着:

javascript
payload.find({
  collection: 'categories',
  where: {
    'relatedPosts.title': { // relatedPosts 是一个关联字段
      equals: "post"
    }
  }
})

此查询尚不支持。

REST API

REST API 支持与本地 API 相同的查询选项。您可以使用 joins 查询参数,通过字段名称自定义每个关联字段的请求。例如,以下 API 调用将获取一个文档,并限制相关帖子数量为 5,并按标题排序:

/api/categories/${id}?joins[relatedPosts][limit]=5&joins[relatedPosts][sort]=title

对于单个请求,您可以根据需要为相同或不同的关联字段指定多个 joins 参数。

GraphQL

GraphQL API 支持与本地和 REST API 相同的查询选项。您可以在查询中为每个关联字段指定查询选项。

例如:

javascript
query {
  Categories {
    docs {
      relatedPosts(
        sort: "createdAt"
        limit: 5
        where: { author: { equals: "66e3431a3f23e684075aaeb9" } }
      ) {
        docs {
          title
        }
        hasNextPage
      }
    }
  }
}