diff --git a/packages/payload/src/fields/hooks/afterRead/virtualFieldPopulationPromise.ts b/packages/payload/src/fields/hooks/afterRead/virtualFieldPopulationPromise.ts index 4c8eb758d1..6b9f9893b0 100644 --- a/packages/payload/src/fields/hooks/afterRead/virtualFieldPopulationPromise.ts +++ b/packages/payload/src/fields/hooks/afterRead/virtualFieldPopulationPromise.ts @@ -85,6 +85,11 @@ export const virtualFieldPopulationPromise = async ({ docID = currentValue } + if (segments[0] === 'id' && segments.length === 0) { + siblingDoc[name] = docID + return + } + if (typeof docID !== 'string' && typeof docID !== 'number') { return } diff --git a/test/database/config.ts b/test/database/config.ts index d1656c347a..806872ee68 100644 --- a/test/database/config.ts +++ b/test/database/config.ts @@ -471,6 +471,16 @@ export default buildConfigWithDefaults({ type: 'text', virtual: 'post.category.title', }, + { + name: 'postCategoryID', + type: 'json', + virtual: 'post.category.id', + }, + { + name: 'postID', + type: 'json', + virtual: 'post.id', + }, { name: 'postLocalized', type: 'text', @@ -481,6 +491,16 @@ export default buildConfigWithDefaults({ type: 'relationship', relationTo: 'posts', }, + { + name: 'customID', + type: 'relationship', + relationTo: 'custom-ids', + }, + { + name: 'customIDValue', + type: 'text', + virtual: 'customID.id', + }, ], versions: { drafts: true }, }, diff --git a/test/database/int.spec.ts b/test/database/int.spec.ts index a4c3210a9a..77e4d83091 100644 --- a/test/database/int.spec.ts +++ b/test/database/int.spec.ts @@ -1993,11 +1993,63 @@ describe('database', () => { collection: 'virtual-relations', depth: 0, where: { id: { equals: id } }, - draft: true, }) expect(draft.docs[0]?.postTitle).toBe('my-title') }) + it('should allow virtual field as reference to ID', async () => { + const post = await payload.create({ collection: 'posts', data: { title: 'my-title' } }) + const { id } = await payload.create({ + collection: 'virtual-relations', + depth: 0, + data: { post: post.id }, + }) + + const docDepth2 = await payload.findByID({ collection: 'virtual-relations', id }) + expect(docDepth2.postID).toBe(post.id) + const docDepth0 = await payload.findByID({ collection: 'virtual-relations', id, depth: 0 }) + expect(docDepth0.postID).toBe(post.id) + }) + + it('should allow virtual field as reference to custom ID', async () => { + const customID = await payload.create({ collection: 'custom-ids', data: {} }) + const { id } = await payload.create({ + collection: 'virtual-relations', + depth: 0, + data: { customID: customID.id }, + }) + + const docDepth2 = await payload.findByID({ collection: 'virtual-relations', id }) + expect(docDepth2.customIDValue).toBe(customID.id) + const docDepth0 = await payload.findByID({ + collection: 'virtual-relations', + id, + depth: 0, + }) + expect(docDepth0.customIDValue).toBe(customID.id) + }) + + it('should allow deep virtual field as reference to ID', async () => { + const category = await payload.create({ + collection: 'categories', + data: { title: 'category-3' }, + }) + const post = await payload.create({ + collection: 'posts', + data: { category: category.id, title: 'my-title-3' }, + }) + const { id } = await payload.create({ + collection: 'virtual-relations', + depth: 0, + data: { post: post.id }, + }) + + const docDepth2 = await payload.findByID({ collection: 'virtual-relations', id }) + expect(docDepth2.postCategoryID).toBe(category.id) + const docDepth0 = await payload.findByID({ collection: 'virtual-relations', id, depth: 0 }) + expect(docDepth0.postCategoryID).toBe(category.id) + }) + it('should allow virtual field with reference localized', async () => { const post = await payload.create({ collection: 'posts', diff --git a/test/database/payload-types.ts b/test/database/payload-types.ts index f29cf028fc..5cdc1faac9 100644 --- a/test/database/payload-types.ts +++ b/test/database/payload-types.ts @@ -373,8 +373,39 @@ export interface VirtualRelation { id: string; postTitle?: string | null; postCategoryTitle?: string | null; + postCategoryID?: + | { + [k: string]: unknown; + } + | unknown[] + | string + | number + | boolean + | null; + postID?: + | { + [k: string]: unknown; + } + | unknown[] + | string + | number + | boolean + | null; postLocalized?: string | null; post?: (string | null) | Post; + customID?: (string | null) | CustomId; + customIDValue?: string | null; + updatedAt: string; + createdAt: string; + _status?: ('draft' | 'published') | null; +} +/** + * This interface was referenced by `Config`'s JSON-Schema + * via the `definition` "custom-ids". + */ +export interface CustomId { + id: string; + title?: string | null; updatedAt: string; createdAt: string; _status?: ('draft' | 'published') | null; @@ -398,17 +429,6 @@ export interface FieldsPersistance { updatedAt: string; createdAt: string; } -/** - * This interface was referenced by `Config`'s JSON-Schema - * via the `definition` "custom-ids". - */ -export interface CustomId { - id: string; - title?: string | null; - updatedAt: string; - createdAt: string; - _status?: ('draft' | 'published') | null; -} /** * This interface was referenced by `Config`'s JSON-Schema * via the `definition` "fake-custom-ids". @@ -807,8 +827,12 @@ export interface PlacesSelect { export interface VirtualRelationsSelect { postTitle?: T; postCategoryTitle?: T; + postCategoryID?: T; + postID?: T; postLocalized?: T; post?: T; + customID?: T; + customIDValue?: T; updatedAt?: T; createdAt?: T; _status?: T;