嵌套文档插件

https://www.npmjs.com/package/@payloadcms/plugin-nested-docs

该插件允许你轻松地将应用中的文档相互嵌套。它通过在每个文档上添加一个新的 parent 字段来实现这一功能,当选择父文档时,当前文档就会附加到父文档的树结构中。例如,当你编辑某个文档的曾曾祖父文档时,其所有后代文档都会递归更新。这是在 collection 中实现层级结构(如页面之间的父子关系)的极其强大的方式。

文档还会获得一个新的 breadcrumbs 字段。一旦分配了父文档,这些面包屑导航就会根据树结构中的每个祖先自动填充。面包屑导航允许你根据文档在层级结构中的位置动态生成标签和 URL。即使父文档的 slug 发生变化,或者整个树结构被嵌套到更深层级,这些变更也会级联影响整个树结构,所有面包屑导航都会相应更新。

通过这种模式,你可以对任意深度嵌套的文档执行应用所需的任何副作用操作。例如,你可以轻松地在每个文档上添加自定义的 fullTitle 字段,并将父文档的标题注入其中,形成类似"父标题 > 子标题"的格式。这样你就可以基于该字段而非原始标题进行搜索和筛选。当你拥有两个标题相同但父文档不同的文档时,这一功能尤其有用。

该插件完全开源,源代码可在此处查看。 如需帮助,请查阅我们的社区帮助。如发现任何问题, 请提交新 issue, 并尽可能提供详细描述。

核心功能

  • 自动为每个文档添加 parent 关联字段
  • 允许同一集合内的文档建立父子关系
  • 当父文档变更时递归更新所有子文档
  • 自动填充 breadcrumbs 字段,包含树形结构中的所有祖先
  • 动态生成每个面包屑的标签和 URL
  • 支持本地化

安装

使用 pnpmnpmYarn 等 JavaScript 包管理器安装插件:

  pnpm add @payloadcms/plugin-nested-docs

基础用法

Payload 配置plugins 数组中,调用插件并传入选项

import { buildConfig } from 'payload'
import { nestedDocsPlugin } from '@payloadcms/plugin-nested-docs'

const config = buildConfig({
  collections: [
    {
      slug: 'pages',
      fields: [
        {
          name: 'title',
          type: 'text',
        },
        {
          name: 'slug',
          type: 'text',
        },
      ],
    },
  ],
  plugins: [
    nestedDocsPlugin({
      collections: ['pages'],
      generateLabel: (_, doc) => doc.title,
      generateURL: (docs) =>
        docs.reduce((url, doc) => `${url}/${doc.slug}`, ''),
    }),
  ],
})

export default config

字段

父级关系

parent 关联字段会自动添加到每个文档中,允许编辑者从同一集合中选择另一个文档作为直接父级。

面包屑导航

breadcrumbs 字段是一个数组,它会动态填充文档的所有父级关系直至顶层,并存储以下字段。

字段描述
label面包屑导航项的标签。该字段会自动设置为 collection.admin.useAsTitle(如果已定义),否则会设置为文档的 ID。你也可以通过向 generateLabel 的 options 属性传递一个函数来动态定义 label
url面包屑导航项的 URL。默认情况下,该字段未定义。你可以通过向插件选项属性 generateURL 传递一个函数来手动定义该字段。

配置选项

collections

一个包含 collection 名称的数组,用于启用嵌套文档功能。

generateLabel

每个 breadcrumb 都有一个必需的 label 字段。默认情况下,其值会被设置为 collection 的 admin.useAsTitle,或者回退到文档的 ID

你也可以传递一个函数来动态设置 breadcrumb 的 label

// payload.config.ts
nestedDocsPlugin({
  //...
  generateLabel: (_, doc) => doc.title, // 注意: 'title' 是一个假设字段
})

该函数接收两个参数并返回一个字符串:

参数类型描述
docsArray到当前点为止的 breadcrumbs 数组
docObject当前正在编辑的文档

generateURL

这个函数允许你动态生成每个 breadcrumb 的 url。每个 breadcrumb 都有一个可选的 url 字段,默认情况下是 undefined。例如,你可能想要格式化一个完整的 URL 来包含到该点为止的所有 breadcrumbs,比如 /about-us/company/our-team

// payload.config.ts
nestedDocsPlugin({
  //...
  generateURL: (docs) => docs.reduce((url, doc) => `${url}/${doc.slug}`, ''), // 注意: 'slug' 是一个假设字段
})
参数类型描述
docsArray到当前点为止的 breadcrumbs 数组
docObject当前正在编辑的文档

parentFieldSlug

当定义此属性时,parent 字段不会自动提供,而是需要你手动为每个 collection 添加自己的 parent 字段。这让你可以完全控制该字段在管理面板中的位置等。将此属性设置为你自定义字段的 name

当定义此属性时,系统将不会自动提供 breadcrumbs 字段,而是需要你手动为每个 collection 添加自己的 breadcrumbs 字段。将此属性设置为你自定义字段的 name

注意:

如果你选择不使用系统自动提供的 parentbreadcrumbs 字段,需要确保这两个字段都位于文档的顶层。它们不能存在于任何嵌套数据结构中,如 grouparrayblocks

覆盖配置

你可以通过使用 createParentFieldcreateBreadcrumbField 方法,在每个 collection 中扩展内置的 parentbreadcrumbs 字段。这些方法会将你的自定义配置与插件的基础字段配置进行合并。

import type { CollectionConfig } from 'payload'
import { createParentField } from '@payloadcms/plugin-nested-docs/fields'
import { createBreadcrumbsField } from '@payloadcms/plugin-nested-docs/fields'

const examplePageConfig: CollectionConfig = {
  slug: 'pages',
  fields: [
    createParentField(
      // 第一个参数等于该字段引用的 collection 的 slug
      'pages',

      // 第二个参数是你指定的字段覆盖配置,
      // 这些配置将与基础 parent 字段配置合并
      {
        admin: {
          position: 'sidebar',
        },
        // 注意:如果你覆盖了 `parent` 字段的 `filterOptions`,
        // 请确保继续阻止文档将自身引用为父级,如下所示:
        // filterOptions: ({ id }) => ({ id: {not_equals: id }})
      },
    ),
    createBreadcrumbsField(
      // 第一个参数等于该字段引用的 collection 的 slug
      'pages',

      // 参数是你指定的字段覆盖配置,
      // 这些配置将与基础 `breadcrumbs` 字段配置合并
      {
        label: '页面面包屑',
      },
    ),
  ],
}

注意:

如果覆盖了 breadcrumbsparent 字段的 name 属性,你必须分别指定 breadcrumbsFieldSlugparentFieldSlug

本地化

本插件默认支持本地化。如果你的 Payload Config 中设置了 localization 属性,breadcrumbs 字段会自动进行本地化。有关 Payload 中本地化工作原理的更多细节,请参阅 本地化 文档。

TypeScript

所有类型都可以直接导入:

import {
  PluginConfig,
  GenerateURL,
  GenerateLabel,
} from '@payloadcms/plugin-nested-docs/types'

示例

Templates Directory 中还包含官方的 Website TemplateE-commerce Template,这两个模板都使用了此插件。