feat: support relationship writes using objects instead of IDs (#9253)
### What?
Previously, this code led to a validation error because `movie` is an
object and you needed to use `movie.id` instead.
```ts
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,
},
},
})
```
While it's simple to modify this example, it's more painful when you
have a data with `depth` > 0 and then you want to update that document.
### Why?
Better DX as less checks needed, and TypeScript says that we can pass an
object.
### How?
Sanitizes the field value in the root `beforeValidate` hook
This commit is contained in:
@@ -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({
|
||||
|
||||
@@ -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', () => {
|
||||
|
||||
@@ -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<false> | PagesSelect<true>;
|
||||
'rels-to-pages': RelsToPagesSelect<false> | RelsToPagesSelect<true>;
|
||||
'rels-to-pages-and-custom-text-ids': RelsToPagesAndCustomTextIdsSelect<false> | RelsToPagesAndCustomTextIdsSelect<true>;
|
||||
'object-writes': ObjectWritesSelect<false> | ObjectWritesSelect<true>;
|
||||
users: UsersSelect<false> | UsersSelect<true>;
|
||||
'payload-locked-documents': PayloadLockedDocumentsSelect<false> | PayloadLockedDocumentsSelect<true>;
|
||||
'payload-preferences': PayloadPreferencesSelect<false> | PayloadPreferencesSelect<true>;
|
||||
@@ -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<T extends boolean = true> {
|
||||
updatedAt?: T;
|
||||
createdAt?: T;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "object-writes_select".
|
||||
*/
|
||||
export interface ObjectWritesSelect<T extends boolean = true> {
|
||||
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".
|
||||
|
||||
Reference in New Issue
Block a user