The module `@payloadcms/plugin-nested-docs/fields` does not seem to exist (anymore). Instead `createParentField` and `createBreadcrumbsField` are exported by `@payloadcms/plugin-nested-docs`
249 lines
10 KiB
Plaintext
249 lines
10 KiB
Plaintext
---
|
|
title: Nested Docs Plugin
|
|
label: Nested Docs
|
|
order: 40
|
|
desc: Nested documents in a parent, child, and sibling relationship.
|
|
keywords: plugins, nested, documents, parent, child, sibling, relationship
|
|
---
|
|
|
|

|
|
|
|
This plugin allows you to easily nest the documents of your application inside of one another. It does so by adding a
|
|
new `parent` field onto each of your documents that, when selected, attaches itself to the parent's tree. When you edit
|
|
the great-great-grandparent of a document, for instance, all of its descendants are recursively updated. This is an
|
|
extremely powerful way of achieving hierarchy within a collection, such as parent/child relationship between pages.
|
|
|
|
Documents also receive a new `breadcrumbs` field. Once a parent is assigned, these breadcrumbs are populated based on
|
|
each ancestor up the tree. Breadcrumbs allow you to dynamically generate labels and URLs based on the document's
|
|
position in the hierarchy. Even if the slug of a parent document changes, or the entire tree is nested another level
|
|
deep, changes will cascade down the entire tree and all breadcrumbs will reflect those changes.
|
|
|
|
With this pattern you can perform whatever side-effects your applications needs on even the most deeply nested
|
|
documents. For example, you could easily add a custom `fullTitle` field onto each document and inject the parent's title
|
|
onto it, such as "Parent Title > Child Title". This would allow you to then perform searches and filters based on _that_
|
|
field instead of the original title. This is especially useful if you happen to have two documents with identical titles
|
|
but different parents.
|
|
|
|
<Banner type="info">
|
|
This plugin is completely open-source and the [source code can be found
|
|
here](https://github.com/payloadcms/payload/tree/main/packages/plugin-nested-docs).
|
|
If you need help, check out our [Community
|
|
Help](https://payloadcms.com/community-help). If you think you've found a bug,
|
|
please [open a new
|
|
issue](https://github.com/payloadcms/payload/issues/new?assignees=&labels=plugin%3A%20nested-docs&template=bug_report.md&title=plugin-nested-docs%3A)
|
|
with as much detail as possible.
|
|
</Banner>
|
|
|
|
## Core features
|
|
|
|
- Automatically adds a `parent` relationship field to each document
|
|
- Allows for parent/child relationships between documents within the same collection
|
|
- Recursively updates all descendants when a parent is changed
|
|
- Automatically populates a `breadcrumbs` field with all ancestors up the tree
|
|
- Dynamically generate labels and URLs for each breadcrumb
|
|
- Supports localization
|
|
|
|
## Installation
|
|
|
|
Install the plugin using any JavaScript package manager like [pnpm](https://pnpm.io), [npm](https://npmjs.com), or [Yarn](https://yarnpkg.com):
|
|
|
|
```bash
|
|
pnpm add @payloadcms/plugin-nested-docs
|
|
```
|
|
|
|
## Basic Usage
|
|
|
|
In the `plugins` array of your [Payload Config](https://payloadcms.com/docs/configuration/overview), call the plugin
|
|
with [options](#options):
|
|
|
|
```ts
|
|
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
|
|
```
|
|
|
|
### Fields
|
|
|
|
#### Parent
|
|
|
|
The `parent` relationship field is automatically added to every document which allows editors to choose another document
|
|
from the same collection to act as the direct parent.
|
|
|
|
#### Breadcrumbs
|
|
|
|
The `breadcrumbs` field is an array which dynamically populates all parent relationships of a document up to the top
|
|
level and stores the following fields.
|
|
|
|
| Field | Description |
|
|
| ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
| `label` | The label of the breadcrumb. This field is automatically set to either the `collection.admin.useAsTitle` (if defined) or is set to the `ID` of the document. You can also dynamically define the `label` by passing a function to the options property of [`generateLabel`](#generatelabel). |
|
|
| `url` | The URL of the breadcrumb. By default, this field is undefined. You can manually define this field by passing a property called function to the plugin options property of [`generateURL`](#generateurl). |
|
|
|
|
### Options
|
|
|
|
#### `collections`
|
|
|
|
An array of collections slugs to enable nested docs.
|
|
|
|
#### `generateLabel`
|
|
|
|
Each `breadcrumb` has a required `label` field. By default, its value will be set to the collection's `admin.useAsTitle`
|
|
or fallback the the `ID` of the document.
|
|
|
|
You can also pass a function to dynamically set the `label` of your breadcrumb.
|
|
|
|
```ts
|
|
// payload.config.ts
|
|
nestedDocsPlugin({
|
|
//...
|
|
generateLabel: (_, doc) => doc.title, // NOTE: 'title' is a hypothetical field
|
|
})
|
|
```
|
|
|
|
The function takes two arguments and returns a string:
|
|
|
|
| Argument | Type | Description |
|
|
| -------- | -------- | -------------------------------------------- |
|
|
| `docs` | `Array` | An array of the breadcrumbs up to that point |
|
|
| `doc` | `Object` | The current document being edited |
|
|
|
|
#### `generateURL`
|
|
|
|
A function that allows you to dynamically generate each breadcrumb `url`. Each `breadcrumb` has an optional `url` field
|
|
which is undefined by default. For example, you might want to format a full URL to contain all breadcrumbs up to
|
|
that point, like `/about-us/company/our-team`.
|
|
|
|
```ts
|
|
// payload.config.ts
|
|
nestedDocsPlugin({
|
|
//...
|
|
generateURL: (docs) => docs.reduce((url, doc) => `${url}/${doc.slug}`, ''), // NOTE: 'slug' is a hypothetical field
|
|
})
|
|
```
|
|
|
|
| Argument | Type | Description |
|
|
| -------- | -------- | -------------------------------------------- |
|
|
| `docs` | `Array` | An array of the breadcrumbs up to that point |
|
|
| `doc` | `Object` | The current document being edited |
|
|
|
|
#### `parentFieldSlug`
|
|
|
|
When defined, the `parent` field will not be provided for you automatically, and instead, expects you to add your
|
|
own `parent` field to each collection manually. This gives you complete control over where you put the field in your
|
|
admin dashboard, etc. Set this property to the `name` of your custom field.
|
|
|
|
#### `breadcrumbsFieldSlug`
|
|
|
|
When defined, the `breadcrumbs` field will not be provided for you, and instead, expects you to add your
|
|
own `breadcrumbs` field to each collection manually. Set this property to the `name` of your custom field.
|
|
|
|
<Banner type="info">
|
|
**Note:**
|
|
|
|
If you opt out of automatically being provided a `parent` or `breadcrumbs` field, you need to make
|
|
sure that both fields are placed at the top-level of your document. They cannot exist within any
|
|
nested data structures like a `group`, `array`, or `blocks`.
|
|
|
|
</Banner>
|
|
|
|
## Overrides
|
|
|
|
You can also extend the built-in `parent` and `breadcrumbs` fields per collection by using the `createParentField`
|
|
and `createBreadcrumbField` methods. They will merge your customizations overtop the plugin's base field configurations.
|
|
|
|
```ts
|
|
import type { CollectionConfig } from 'payload'
|
|
import { createParentField } from '@payloadcms/plugin-nested-docs'
|
|
import { createBreadcrumbsField } from '@payloadcms/plugin-nested-docs'
|
|
|
|
const examplePageConfig: CollectionConfig = {
|
|
slug: 'pages',
|
|
fields: [
|
|
createParentField(
|
|
// First argument is equal to the slug of the collection
|
|
// that the field references
|
|
'pages',
|
|
|
|
// Second argument is equal to field overrides that you specify,
|
|
// which will be merged into the base parent field config
|
|
{
|
|
admin: {
|
|
position: 'sidebar',
|
|
},
|
|
// Note: if you override the `filterOptions` of the `parent` field,
|
|
// be sure to continue to prevent the document from referencing itself as the parent like this:
|
|
// filterOptions: ({ id }) => ({ id: {not_equals: id }})
|
|
},
|
|
),
|
|
createBreadcrumbsField(
|
|
// First argument is equal to the slug of the collection
|
|
// that the field references
|
|
'pages',
|
|
|
|
// Argument equal to field overrides that you specify,
|
|
// which will be merged into the base `breadcrumbs` field config
|
|
{
|
|
label: 'Page Breadcrumbs',
|
|
},
|
|
),
|
|
],
|
|
}
|
|
```
|
|
|
|
<Banner type="warning">
|
|
**Note:**
|
|
|
|
If overriding the `name` of either `breadcrumbs` or `parent` fields, you must specify the
|
|
`breadcrumbsFieldSlug` or `parentFieldSlug` respectively.
|
|
|
|
</Banner>
|
|
|
|
## Localization
|
|
|
|
This plugin supports localization by default. If the `localization` property is set in your Payload Config,
|
|
the `breadcrumbs` field is automatically localized. For more details on how localization works in Payload, see
|
|
the [Localization](https://payloadcms.com/docs/configuration/localization) docs.
|
|
|
|
## TypeScript
|
|
|
|
All types can be directly imported:
|
|
|
|
```ts
|
|
import {
|
|
PluginConfig,
|
|
GenerateURL,
|
|
GenerateLabel,
|
|
} from '@payloadcms/plugin-nested-docs/types'
|
|
```
|
|
|
|
## Examples
|
|
|
|
The [Templates Directory](https://github.com/payloadcms/payload/tree/main/templates) also contains an official [Website Template](https://github.com/payloadcms/payload/tree/main/templates/website) and [E-commerce Template](https://github.com/payloadcms/payload/tree/main/templates/ecommerce), both of which use this plugin.
|