Files
payloadcms/packages/richtext-slate/src/data/recurseNestedFields.ts
Sasha a22c0e62fa feat: add populate property to Local / REST API (#8969)
### What?
Adds `populate` property to Local API and REST API operations that can
be used to specify `select` for a specific collection when it's
populated
```ts
const result = await payload.findByID({
  populate: {
   // type safe if you have generated types
    posts: {
      text: true,
    },
  },
  collection: 'pages',
  depth: 1,
  id: aboutPage.id,
})

result.relatedPost // only has text and id properties
``` 

```ts
fetch('https://localhost:3000/api/pages?populate[posts][text]=true') // highlight-line
  .then((res) => res.json())
  .then((data) => console.log(data))
```

It also overrides
[`defaultPopulate`](https://github.com/payloadcms/payload/pull/8934)

Ensures `defaultPopulate` doesn't affect GraphQL.

### How?
Implements the property for all operations that have the `depth`
argument.
2024-11-06 13:50:19 -05:00

230 lines
6.5 KiB
TypeScript

import type { Field, PayloadRequest, PopulateType } from 'payload'
import { fieldAffectsData, fieldHasSubFields, fieldIsArrayType, tabHasName } from 'payload/shared'
import { populate } from './populate.js'
import { recurseRichText } from './richTextRelationshipPromise.js'
type NestedRichTextFieldsArgs = {
currentDepth?: number
data: unknown
depth: number
draft: boolean
fields: Field[]
overrideAccess: boolean
populateArg?: PopulateType
populationPromises: Promise<void>[]
req: PayloadRequest
showHiddenFields: boolean
}
export const recurseNestedFields = ({
currentDepth = 0,
data,
depth,
draft,
fields,
overrideAccess = false,
populateArg,
populationPromises,
req,
showHiddenFields,
}: NestedRichTextFieldsArgs): void => {
fields.forEach((field) => {
if (field.type === 'relationship' || field.type === 'upload') {
if (field.type === 'relationship') {
if (field.hasMany && Array.isArray(data[field.name])) {
if (Array.isArray(field.relationTo)) {
data[field.name].forEach(({ relationTo, value }, i) => {
const collection = req.payload.collections[relationTo]
if (collection) {
populationPromises.push(
populate({
id: value,
collection,
currentDepth,
data: data[field.name],
depth,
draft,
field,
key: i,
overrideAccess,
req,
select:
populateArg?.[collection.config.slug] ?? collection.config.defaultPopulate,
showHiddenFields,
}),
)
}
})
} else {
data[field.name].forEach((id, i) => {
const collection = req.payload.collections[field.relationTo as string]
if (collection) {
populationPromises.push(
populate({
id,
collection,
currentDepth,
data: data[field.name],
depth,
draft,
field,
key: i,
overrideAccess,
req,
select:
populateArg?.[collection.config.slug] ?? collection.config.defaultPopulate,
showHiddenFields,
}),
)
}
})
}
} else if (
Array.isArray(field.relationTo) &&
data[field.name]?.value &&
data[field.name]?.relationTo
) {
if (!('hasMany' in field) || !field.hasMany) {
const collection = req.payload.collections[data[field.name].relationTo]
populationPromises.push(
populate({
id: data[field.name].value,
collection,
currentDepth,
data: data[field.name],
depth,
draft,
field,
key: 'value',
overrideAccess,
req,
select: populateArg?.[collection.config.slug] ?? collection.config.defaultPopulate,
showHiddenFields,
}),
)
}
}
}
if (typeof data[field.name] !== 'undefined' && typeof field.relationTo === 'string') {
const collection = req.payload.collections[field.relationTo]
populationPromises.push(
populate({
id: data[field.name],
collection,
currentDepth,
data,
depth,
draft,
field,
key: field.name,
overrideAccess,
req,
select: populateArg?.[collection.config.slug] ?? collection.config.defaultPopulate,
showHiddenFields,
}),
)
}
} else if (fieldHasSubFields(field) && !fieldIsArrayType(field)) {
if (fieldAffectsData(field) && typeof data[field.name] === 'object') {
recurseNestedFields({
currentDepth,
data: data[field.name],
depth,
draft,
fields: field.fields,
overrideAccess,
populateArg,
populationPromises,
req,
showHiddenFields,
})
} else {
recurseNestedFields({
currentDepth,
data,
depth,
draft,
fields: field.fields,
overrideAccess,
populateArg,
populationPromises,
req,
showHiddenFields,
})
}
} else if (field.type === 'tabs') {
field.tabs.forEach((tab) => {
recurseNestedFields({
currentDepth,
data: tabHasName(tab) ? data[tab.name] : data,
depth,
draft,
fields: tab.fields,
overrideAccess,
populateArg,
populationPromises,
req,
showHiddenFields,
})
})
} else if (Array.isArray(data[field.name])) {
if (field.type === 'blocks') {
data[field.name].forEach((row, i) => {
const block = field.blocks.find(({ slug }) => slug === row?.blockType)
if (block) {
recurseNestedFields({
currentDepth,
data: data[field.name][i],
depth,
draft,
fields: block.fields,
overrideAccess,
populateArg,
populationPromises,
req,
showHiddenFields,
})
}
})
}
if (field.type === 'array') {
data[field.name].forEach((_, i) => {
recurseNestedFields({
currentDepth,
data: data[field.name][i],
depth,
draft,
fields: field.fields,
overrideAccess,
populateArg,
populationPromises,
req,
showHiddenFields,
})
})
}
}
if (field.type === 'richText' && Array.isArray(data[field.name])) {
data[field.name].forEach((node) => {
if (Array.isArray(node.children)) {
recurseRichText({
children: node.children,
currentDepth,
depth,
draft,
field,
overrideAccess,
populationPromises,
req,
showHiddenFields,
})
}
})
}
})
}