Skip to content

Commit

Permalink
feat: add defaultPopulate property to collection config (#8934)
Browse files Browse the repository at this point in the history
### What?
Adds `defaultPopulate` property to collection config that allows to
specify which fields to select when the collection is populated from
another document.
```ts
import type { CollectionConfig } from 'payload'

// The TSlug generic can be passed to have type safety for `defaultPopulate`.
// If avoided, the `defaultPopulate` type resolves to `SelectType`.
export const Pages: CollectionConfig<'pages'> = {
  slug: 'pages',
  // I need only slug, NOT the WHOLE CONTENT!
  defaultPopulate: {
    slug: true,
  },
  fields: [
    {
      name: 'slug',
      type: 'text',
      required: true,
    },
  ],
}
```

### Why?
This is essential for example in case of links. You don't need the whole
document, which can contain large data but only the `slug`.

### How?
Implements `defaultPopulate` when populating relationships, including
inside of lexical / slate rich text fields.
  • Loading branch information
r1tsuu authored Oct 30, 2024
1 parent d38d7b8 commit c41ef65
Show file tree
Hide file tree
Showing 15 changed files with 375 additions and 5 deletions.
3 changes: 2 additions & 1 deletion docs/configuration/collections.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ export const Posts: CollectionConfig = {
The following options are available:

| Option | Description |
|------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| ---------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **`admin`** | The configuration options for the Admin Panel. [More details](../admin/collections). |
| **`access`** | Provide Access Control functions to define exactly who should be able to do what with Documents in this Collection. [More details](../access-control/collections). |
| **`auth`** | Specify options if you would like this Collection to feature authentication. [More details](../authentication/overview). |
Expand All @@ -77,6 +77,7 @@ The following options are available:
| **`typescript`** | An object with property `interface` as the text used in schema generation. Auto-generated from slug if not defined. |
| **`upload`** | Specify options if you would like this Collection to support file uploads. For more, consult the [Uploads](../upload/overview) documentation. |
| **`versions`** | Set to true to enable default options, or configure with object properties. [More details](../versions/overview#collection-config). |
| **`defaultPopulate`** | Specify which fields to select when this Collection is populated from another document. [More Details](../queries/select#defaultpopulate-collection-config-property). |

_\* An asterisk denotes that a property is required._

Expand Down
31 changes: 31 additions & 0 deletions docs/queries/select.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -97,3 +97,34 @@ const getPosts = async () => {
<strong>Reminder:</strong>
This is the same for [Globals](../configuration/globals) using the `/api/globals` endpoint.
</Banner>
```


## `defaultPopulate` collection config property

The `defaultPopulate` property allows you specify which fields to select when populating the collection from another document.
This is especially useful for links where only the `slug` is needed instead of the entire document.

```ts
import type { CollectionConfig } from 'payload'

import { lexicalEditor, LinkFeature } from '@payloadcms/richtext-lexical'
import { slateEditor } from '@payloadcms/richtext-slate'

// The TSlug generic can be passed to have type safety for `defaultPopulate`.
// If avoided, the `defaultPopulate` type resolves to `SelectType`.
export const Pages: CollectionConfig<'pages'> = {
slug: 'pages',
// Specify `select`.
defaultPopulate: {
slug: true,
},
fields: [
{
name: 'slug',
type: 'text',
required: true,
},
],
}
```
5 changes: 4 additions & 1 deletion packages/payload/src/collections/config/types.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { GraphQLInputObjectType, GraphQLNonNull, GraphQLObjectType } from 'graphql'
import type { DeepRequired, MarkOptional } from 'ts-essentials'
import type { DeepRequired, IsAny, MarkOptional } from 'ts-essentials'

import type {
CustomPreviewButton,
Expand Down Expand Up @@ -382,6 +382,9 @@ export type CollectionConfig<TSlug extends CollectionSlug = any> = {
* @WARNING: If you change this property with existing data, you will need to handle the renaming of the table in your database or by using migrations
*/
dbName?: DBIdentifierName
defaultPopulate?: IsAny<SelectFromCollectionSlug<TSlug>> extends true
? SelectType
: SelectFromCollectionSlug<TSlug>
/**
* Default field to sort by in collection list view
*/
Expand Down
10 changes: 9 additions & 1 deletion packages/payload/src/collections/dataloader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import type { BatchLoadFn } from 'dataloader'

import DataLoader from 'dataloader'

import type { PayloadRequest } from '../types/index.js'
import type { PayloadRequest, SelectType } from '../types/index.js'
import type { TypeWithID } from './config/types.js'

import { isValidID } from '../utilities/isValidID.js'
Expand Down Expand Up @@ -55,6 +55,7 @@ const batchAndLoadDocs =
overrideAccess,
showHiddenFields,
draft,
select,
] = JSON.parse(key)

const batchKeyArray = [
Expand All @@ -67,6 +68,7 @@ const batchAndLoadDocs =
overrideAccess,
showHiddenFields,
draft,
select,
]

const batchKey = JSON.stringify(batchKeyArray)
Expand Down Expand Up @@ -103,6 +105,7 @@ const batchAndLoadDocs =
overrideAccess,
showHiddenFields,
draft,
select,
] = JSON.parse(batchKey)

req.transactionID = transactionID
Expand All @@ -118,6 +121,7 @@ const batchAndLoadDocs =
overrideAccess: Boolean(overrideAccess),
pagination: false,
req,
select,
showHiddenFields: Boolean(showHiddenFields),
where: {
id: {
Expand All @@ -139,6 +143,7 @@ const batchAndLoadDocs =
fallbackLocale,
locale,
overrideAccess,
select,
showHiddenFields,
transactionID: req.transactionID,
})
Expand Down Expand Up @@ -167,6 +172,7 @@ type CreateCacheKeyArgs = {
fallbackLocale: string
locale: string
overrideAccess: boolean
select?: SelectType
showHiddenFields: boolean
transactionID: number | Promise<number | string> | string
}
Expand All @@ -179,6 +185,7 @@ export const createDataloaderCacheKey = ({
fallbackLocale,
locale,
overrideAccess,
select,
showHiddenFields,
transactionID,
}: CreateCacheKeyArgs): string =>
Expand All @@ -193,4 +200,5 @@ export const createDataloaderCacheKey = ({
overrideAccess,
showHiddenFields,
draft,
select,
])
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ const populate = async ({
fallbackLocale,
locale,
overrideAccess,
select: relatedCollection.config.defaultPopulate,
showHiddenFields,
transactionID: req.transactionID,
}),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ export const RelationshipFeature = createServerFeature<
key: 'value',
overrideAccess,
req,
select: collection.config.defaultPopulate,
showHiddenFields,
}),
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,7 @@ export const UploadFeature = createServerFeature<
key: 'value',
overrideAccess,
req,
select: collection.config.defaultPopulate,
showHiddenFields,
}),
)
Expand Down
5 changes: 4 additions & 1 deletion packages/richtext-lexical/src/populateGraphQL/populate.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { PayloadRequest } from 'payload'
import type { PayloadRequest, SelectType } from 'payload'

import { createDataloaderCacheKey } from 'payload'

Expand All @@ -10,6 +10,7 @@ type Arguments = {
key: number | string
overrideAccess: boolean
req: PayloadRequest
select?: SelectType
showHiddenFields: boolean
}

Expand All @@ -23,6 +24,7 @@ export const populate = async ({
key,
overrideAccess,
req,
select,
showHiddenFields,
}: {
collectionSlug: string
Expand All @@ -46,6 +48,7 @@ export const populate = async ({
fallbackLocale: req.fallbackLocale!,
locale: req.locale!,
overrideAccess,
select,
showHiddenFields,
transactionID: req.transactionID!,
}),
Expand Down
5 changes: 4 additions & 1 deletion packages/richtext-slate/src/data/populate.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { Collection, Field, PayloadRequest, RichTextField } from 'payload'
import type { Collection, Field, PayloadRequest, RichTextField, SelectType } from 'payload'

import { createDataloaderCacheKey } from 'payload'

Expand All @@ -13,6 +13,7 @@ type Arguments = {
key: number | string
overrideAccess?: boolean
req: PayloadRequest
select?: SelectType
showHiddenFields: boolean
}

Expand All @@ -26,6 +27,7 @@ export const populate = async ({
key,
overrideAccess,
req,
select,
showHiddenFields,
}: {
collection: Collection
Expand All @@ -44,6 +46,7 @@ export const populate = async ({
fallbackLocale: req.locale,
locale: req.fallbackLocale,
overrideAccess: typeof overrideAccess === 'undefined' ? false : overrideAccess,
select,
showHiddenFields,
transactionID: req.transactionID,
}),
Expand Down
4 changes: 4 additions & 0 deletions packages/richtext-slate/src/data/recurseNestedFields.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ export const recurseNestedFields = ({
key: i,
overrideAccess,
req,
select: collection.config.defaultPopulate,
showHiddenFields,
}),
)
Expand All @@ -69,6 +70,7 @@ export const recurseNestedFields = ({
key: i,
overrideAccess,
req,
select: collection.config.defaultPopulate,
showHiddenFields,
}),
)
Expand All @@ -94,6 +96,7 @@ export const recurseNestedFields = ({
key: 'value',
overrideAccess,
req,
select: collection.config.defaultPopulate,
showHiddenFields,
}),
)
Expand All @@ -114,6 +117,7 @@ export const recurseNestedFields = ({
key: field.name,
overrideAccess,
req,
select: collection.config.defaultPopulate,
showHiddenFields,
}),
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ export const recurseRichText = ({
key: 'value',
overrideAccess,
req,
select: collection.config.defaultPopulate,
showHiddenFields,
}),
)
Expand Down Expand Up @@ -93,6 +94,7 @@ export const recurseRichText = ({
key: 'value',
overrideAccess,
req,
select: collection.config.defaultPopulate,
showHiddenFields,
}),
)
Expand Down
84 changes: 84 additions & 0 deletions test/select/collections/Pages/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import type { CollectionConfig } from 'payload'

import { lexicalEditor, LinkFeature } from '@payloadcms/richtext-lexical'
import { slateEditor } from '@payloadcms/richtext-slate'

// The TSlug generic can be passed to have type safety for `defaultPopulate`.
// If avoided, the `defaultPopulate` type resolves to `SelectType`.
export const Pages: CollectionConfig<'pages'> = {
slug: 'pages',
// I need only slug, NOT the WHOLE CONTENT!
defaultPopulate: {
slug: true,
},
fields: [
{
name: 'content',
type: 'blocks',
blocks: [
{
slug: 'cta',
fields: [
{
name: 'title',
type: 'text',
required: true,
},
{
name: 'link',
type: 'group',
fields: [
{
name: 'docPoly',
type: 'relationship',
relationTo: ['pages'],
},
{
name: 'doc',
type: 'relationship',
relationTo: 'pages',
},
{
name: 'docMany',
hasMany: true,
type: 'relationship',
relationTo: 'pages',
},
{
name: 'docHasManyPoly',
type: 'relationship',
relationTo: ['pages'],
hasMany: true,
},
{
name: 'label',
type: 'text',
required: true,
},
],
},
{
name: 'richTextLexical',
type: 'richText',
editor: lexicalEditor({
features({ defaultFeatures }) {
return [...defaultFeatures, LinkFeature({ enabledCollections: ['pages'] })]
},
}),
},
{
name: 'richTextSlate',
type: 'richText',
editor: slateEditor({}),
},
],
},
],
},
{
name: 'slug',
type: 'text',
required: true,
},
],
}
2 changes: 2 additions & 0 deletions test/select/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { buildConfigWithDefaults } from '../buildConfigWithDefaults.js'
import { devUser } from '../credentials.js'
import { DeepPostsCollection } from './collections/DeepPosts/index.js'
import { LocalizedPostsCollection } from './collections/LocalizedPosts/index.js'
import { Pages } from './collections/Pages/index.js'
import { PostsCollection } from './collections/Posts/index.js'
import { VersionedPostsCollection } from './collections/VersionedPosts/index.js'

Expand All @@ -19,6 +20,7 @@ export default buildConfigWithDefaults({
LocalizedPostsCollection,
VersionedPostsCollection,
DeepPostsCollection,
Pages,
],
globals: [
{
Expand Down
Loading

0 comments on commit c41ef65

Please sign in to comment.