diff --git a/packages/payload/src/fields/hooks/beforeValidate/promise.ts b/packages/payload/src/fields/hooks/beforeValidate/promise.ts index 75308cbd8..68e19c058 100644 --- a/packages/payload/src/fields/hooks/beforeValidate/promise.ts +++ b/packages/payload/src/fields/hooks/beforeValidate/promise.ts @@ -1,5 +1,5 @@ import type { RichTextAdapter } from '../../../admin/RichText.js' -import type { SanitizedCollectionConfig } from '../../../collections/config/types.js' +import type { SanitizedCollectionConfig, TypeWithID } from '../../../collections/config/types.js' import type { SanitizedGlobalConfig } from '../../../globals/config/types.js' import type { RequestContext } from '../../../index.js' import type { JsonObject, JsonValue, PayloadRequest } from '../../../types/index.js' @@ -91,7 +91,6 @@ export const promise = async ({ // Sanitize incoming data switch (field.type) { case 'array': - case 'blocks': { // Handle cases of arrays being intentionally set to 0 if (siblingData[field.name] === '0' || siblingData[field.name] === 0) { @@ -140,7 +139,6 @@ export const promise = async ({ break } case 'relationship': - case 'upload': { if ( siblingData[field.name] === '' || @@ -163,6 +161,15 @@ export const promise = async ({ const relatedCollection = req.payload.config.collections.find( (collection) => collection.slug === relatedDoc.relationTo, ) + + if ( + typeof relatedDoc.value === 'object' && + relatedDoc.value && + 'id' in relatedDoc.value + ) { + relatedDoc.value = relatedDoc.value.id + } + if (relatedCollection?.fields) { const relationshipIDField = relatedCollection.fields.find( (collectionField) => @@ -181,6 +188,11 @@ export const promise = async ({ const relatedCollection = req.payload.config.collections.find( (collection) => collection.slug === value.relationTo, ) + + if (typeof value.value === 'object' && value.value && 'id' in value.value) { + value.value = (value.value as TypeWithID).id + } + if (relatedCollection?.fields) { const relationshipIDField = relatedCollection.fields.find( (collectionField) => @@ -198,6 +210,10 @@ export const promise = async ({ (collection) => collection.slug === field.relationTo, ) + if (typeof relatedDoc === 'object' && relatedDoc && 'id' in relatedDoc) { + value[i] = relatedDoc.id + } + if (relatedCollection?.fields) { const relationshipIDField = relatedCollection.fields.find( (collectionField) => @@ -214,6 +230,10 @@ export const promise = async ({ (collection) => collection.slug === field.relationTo, ) + if (typeof value === 'object' && value && 'id' in value) { + siblingData[field.name] = value.id + } + if (relatedCollection?.fields) { const relationshipIDField = relatedCollection.fields.find( (collectionField) => @@ -375,7 +395,6 @@ export const promise = async ({ } case 'collapsible': - case 'row': { await traverseFields({ id, diff --git a/test/relationships/config.ts b/test/relationships/config.ts index 4db905a76..7431af805 100644 --- a/test/relationships/config.ts +++ b/test/relationships/config.ts @@ -341,6 +341,33 @@ export default buildConfigWithDefaults({ }, ], }, + { + slug: 'object-writes', + fields: [ + { + type: 'relationship', + relationTo: 'movies', + name: 'one', + }, + { + type: 'relationship', + relationTo: 'movies', + name: 'many', + hasMany: true, + }, + { + type: 'relationship', + relationTo: ['movies'], + name: 'onePoly', + }, + { + type: 'relationship', + relationTo: ['movies'], + name: 'manyPoly', + hasMany: true, + }, + ], + }, ], onInit: async (payload) => { await payload.create({ diff --git a/test/relationships/int.spec.ts b/test/relationships/int.spec.ts index e5f162d09..33a1f29de 100644 --- a/test/relationships/int.spec.ts +++ b/test/relationships/int.spec.ts @@ -1181,7 +1181,7 @@ describe('Relationships', () => { }) }) - describe('Creating', () => { + describe('Writing', () => { describe('With transactions', () => { it('should be able to create filtered relations within a transaction', async () => { const req = {} as PayloadRequest @@ -1208,6 +1208,52 @@ describe('Relationships', () => { expect(withRelation.filteredRelation.id).toEqual(related.id) }) }) + + describe('With passing an object', () => { + it('should create with passing an object', async () => { + const movie = await payload.create({ collection: 'movies', data: {} }) + const result = await payload.create({ + collection: 'object-writes', + data: { + many: [movie], + manyPoly: [{ relationTo: 'movies', value: movie }], + one: movie, + onePoly: { + relationTo: 'movies', + value: movie, + }, + }, + }) + + expect(result.many[0]).toStrictEqual(movie) + expect(result.one).toStrictEqual(movie) + expect(result.manyPoly[0]).toStrictEqual({ relationTo: 'movies', value: movie }) + expect(result.onePoly).toStrictEqual({ relationTo: 'movies', value: movie }) + }) + + it('should update with passing an object', async () => { + const movie = await payload.create({ collection: 'movies', data: {} }) + const { id } = await payload.create({ collection: 'object-writes', data: {} }) + const result = await payload.update({ + collection: 'object-writes', + id, + data: { + many: [movie], + manyPoly: [{ relationTo: 'movies', value: movie }], + one: movie, + onePoly: { + relationTo: 'movies', + value: movie, + }, + }, + }) + + expect(result.many[0]).toStrictEqual(movie) + expect(result.one).toStrictEqual(movie) + expect(result.manyPoly[0]).toStrictEqual({ relationTo: 'movies', value: movie }) + expect(result.onePoly).toStrictEqual({ relationTo: 'movies', value: movie }) + }) + }) }) describe('Polymorphic Relationships', () => { diff --git a/test/relationships/payload-types.ts b/test/relationships/payload-types.ts index 6b788fa75..a9d0d940e 100644 --- a/test/relationships/payload-types.ts +++ b/test/relationships/payload-types.ts @@ -27,6 +27,7 @@ export interface Config { pages: Page; 'rels-to-pages': RelsToPage; 'rels-to-pages-and-custom-text-ids': RelsToPagesAndCustomTextId; + 'object-writes': ObjectWrite; users: User; 'payload-locked-documents': PayloadLockedDocument; 'payload-preferences': PayloadPreference; @@ -50,6 +51,7 @@ export interface Config { pages: PagesSelect | PagesSelect; 'rels-to-pages': RelsToPagesSelect | RelsToPagesSelect; 'rels-to-pages-and-custom-text-ids': RelsToPagesAndCustomTextIdsSelect | RelsToPagesAndCustomTextIdsSelect; + 'object-writes': ObjectWritesSelect | ObjectWritesSelect; users: UsersSelect | UsersSelect; 'payload-locked-documents': PayloadLockedDocumentsSelect | PayloadLockedDocumentsSelect; 'payload-preferences': PayloadPreferencesSelect | PayloadPreferencesSelect; @@ -311,6 +313,27 @@ export interface RelsToPagesAndCustomTextId { updatedAt: string; createdAt: string; } +/** + * This interface was referenced by `Config`'s JSON-Schema + * via the `definition` "object-writes". + */ +export interface ObjectWrite { + id: string; + one?: (string | null) | Movie; + many?: (string | Movie)[] | null; + onePoly?: { + relationTo: 'movies'; + value: string | Movie; + } | null; + manyPoly?: + | { + relationTo: 'movies'; + value: string | Movie; + }[] + | null; + updatedAt: string; + createdAt: string; +} /** * This interface was referenced by `Config`'s JSON-Schema * via the `definition` "payload-locked-documents". @@ -382,6 +405,10 @@ export interface PayloadLockedDocument { relationTo: 'rels-to-pages-and-custom-text-ids'; value: string | RelsToPagesAndCustomTextId; } | null) + | ({ + relationTo: 'object-writes'; + value: string | ObjectWrite; + } | null) | ({ relationTo: 'users'; value: string | User; @@ -609,6 +636,18 @@ export interface RelsToPagesAndCustomTextIdsSelect { updatedAt?: T; createdAt?: T; } +/** + * This interface was referenced by `Config`'s JSON-Schema + * via the `definition` "object-writes_select". + */ +export interface ObjectWritesSelect { + one?: T; + many?: T; + onePoly?: T; + manyPoly?: T; + updatedAt?: T; + createdAt?: T; +} /** * This interface was referenced by `Config`'s JSON-Schema * via the `definition` "users_select".