fix!: handle custom id logic in mongodb adapter (#9069)
### What? Moved the logic for copying the data.id to data._id to the mongoose adapter. ### Why? If you have any hooks that need to set the `id`, the value does not get sent to mongodb as you would expect since it was copied before the beforeValidate hooks. ### How? Now data._id is assigned only in the mongodb adapter's `create` function. BREAKING CHANGES: When using custom ID fields, if you have any collection hooks for beforeValidate, beforeChange then `data._id` will no longer be assigned as this happens now in the database adapter. Use `data.id` instead.
This commit is contained in:
@@ -356,7 +356,7 @@ For full details on Admin Options, see the [Field Admin Options](../admin/fields
|
||||
|
||||
## Custom ID Fields
|
||||
|
||||
All [Collections](../configuration/collections) automatically generate their own ID field. If needed, you can override this behavior by providing an explicit ID field to your config. This will force users to provide a their own ID value when creating a record.
|
||||
All [Collections](../configuration/collections) automatically generate their own ID field. If needed, you can override this behavior by providing an explicit ID field to your config. This field should either be required or have a hook to generate the ID dynamically.
|
||||
|
||||
To define a custom ID field, add a new field with the `name` property set to `id`:
|
||||
|
||||
@@ -368,6 +368,7 @@ export const MyCollection: CollectionConfig = {
|
||||
fields: [
|
||||
{
|
||||
name: 'id', // highlight-line
|
||||
required: true,
|
||||
type: 'number',
|
||||
},
|
||||
],
|
||||
|
||||
@@ -20,6 +20,10 @@ export const create: Create = async function create(
|
||||
fields: this.payload.collections[collection].config.fields,
|
||||
})
|
||||
|
||||
if (this.payload.collections[collection].customIDType) {
|
||||
sanitizedData._id = sanitizedData.id
|
||||
}
|
||||
|
||||
try {
|
||||
;[doc] = await Model.create([sanitizedData], options)
|
||||
} catch (error) {
|
||||
|
||||
@@ -123,17 +123,6 @@ export const createOperation = async <
|
||||
await executeAccess({ data, req }, collectionConfig.access.create)
|
||||
}
|
||||
|
||||
// /////////////////////////////////////
|
||||
// Custom id
|
||||
// /////////////////////////////////////
|
||||
|
||||
if (payload.collections[collectionConfig.slug].customIDType) {
|
||||
data = {
|
||||
_id: data.id,
|
||||
...data,
|
||||
}
|
||||
}
|
||||
|
||||
// /////////////////////////////////////
|
||||
// Generate data for all files and sizes
|
||||
// /////////////////////////////////////
|
||||
|
||||
@@ -4,6 +4,8 @@ const filename = fileURLToPath(import.meta.url)
|
||||
const dirname = path.dirname(filename)
|
||||
import type { TextField } from 'payload'
|
||||
|
||||
import { v4 as uuid } from 'uuid'
|
||||
|
||||
import { buildConfigWithDefaults } from '../buildConfigWithDefaults.js'
|
||||
import { devUser } from '../credentials.js'
|
||||
|
||||
@@ -356,6 +358,29 @@ export default buildConfigWithDefaults({
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
slug: 'custom-ids',
|
||||
fields: [
|
||||
{
|
||||
name: 'id',
|
||||
type: 'text',
|
||||
admin: {
|
||||
readOnly: true,
|
||||
},
|
||||
hooks: {
|
||||
beforeChange: [
|
||||
({ value, operation }) => {
|
||||
if (operation === 'create') {
|
||||
return uuid()
|
||||
}
|
||||
return value
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
versions: { drafts: true },
|
||||
},
|
||||
],
|
||||
globals: [
|
||||
{
|
||||
|
||||
@@ -75,10 +75,19 @@ describe('database', () => {
|
||||
|
||||
expect(updated.id).toStrictEqual(created.doc.id)
|
||||
})
|
||||
|
||||
it('should create with generated ID text from hook', async () => {
|
||||
const doc = await payload.create({
|
||||
collection: 'custom-ids',
|
||||
data: {},
|
||||
})
|
||||
|
||||
expect(doc.id).toBeDefined()
|
||||
})
|
||||
})
|
||||
|
||||
describe('timestamps', () => {
|
||||
it('should have createdAt and updatedAt timetstamps to the millisecond', async () => {
|
||||
it('should have createdAt and updatedAt timestamps to the millisecond', async () => {
|
||||
const result = await payload.create({
|
||||
collection: 'posts',
|
||||
data: {
|
||||
|
||||
@@ -19,21 +19,44 @@ export interface Config {
|
||||
'custom-schema': CustomSchema;
|
||||
places: Place;
|
||||
'fields-persistance': FieldsPersistance;
|
||||
'custom-ids': CustomId;
|
||||
users: User;
|
||||
'payload-locked-documents': PayloadLockedDocument;
|
||||
'payload-preferences': PayloadPreference;
|
||||
'payload-migrations': PayloadMigration;
|
||||
};
|
||||
collectionsSelect: {
|
||||
posts: PostsSelect<false> | PostsSelect<true>;
|
||||
'default-values': DefaultValuesSelect<false> | DefaultValuesSelect<true>;
|
||||
'relation-a': RelationASelect<false> | RelationASelect<true>;
|
||||
'relation-b': RelationBSelect<false> | RelationBSelect<true>;
|
||||
'pg-migrations': PgMigrationsSelect<false> | PgMigrationsSelect<true>;
|
||||
'custom-schema': CustomSchemaSelect<false> | CustomSchemaSelect<true>;
|
||||
places: PlacesSelect<false> | PlacesSelect<true>;
|
||||
'fields-persistance': FieldsPersistanceSelect<false> | FieldsPersistanceSelect<true>;
|
||||
'custom-ids': CustomIdsSelect<false> | CustomIdsSelect<true>;
|
||||
users: UsersSelect<false> | UsersSelect<true>;
|
||||
'payload-locked-documents': PayloadLockedDocumentsSelect<false> | PayloadLockedDocumentsSelect<true>;
|
||||
'payload-preferences': PayloadPreferencesSelect<false> | PayloadPreferencesSelect<true>;
|
||||
'payload-migrations': PayloadMigrationsSelect<false> | PayloadMigrationsSelect<true>;
|
||||
};
|
||||
db: {
|
||||
defaultIDType: string;
|
||||
};
|
||||
globals: {
|
||||
global: Global;
|
||||
};
|
||||
globalsSelect: {
|
||||
global: GlobalSelect<false> | GlobalSelect<true>;
|
||||
};
|
||||
locale: 'en' | 'es';
|
||||
user: User & {
|
||||
collection: 'users';
|
||||
};
|
||||
jobs?: {
|
||||
tasks: unknown;
|
||||
workflows?: unknown;
|
||||
};
|
||||
}
|
||||
export interface UserAuthOperations {
|
||||
forgotPassword: {
|
||||
@@ -232,6 +255,15 @@ 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;
|
||||
updatedAt: string;
|
||||
createdAt: string;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "users".
|
||||
@@ -288,6 +320,10 @@ export interface PayloadLockedDocument {
|
||||
relationTo: 'fields-persistance';
|
||||
value: string | FieldsPersistance;
|
||||
} | null)
|
||||
| ({
|
||||
relationTo: 'custom-ids';
|
||||
value: string | CustomId;
|
||||
} | null)
|
||||
| ({
|
||||
relationTo: 'users';
|
||||
value: string | User;
|
||||
@@ -334,6 +370,215 @@ export interface PayloadMigration {
|
||||
updatedAt: string;
|
||||
createdAt: string;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "posts_select".
|
||||
*/
|
||||
export interface PostsSelect<T extends boolean = true> {
|
||||
title?: T;
|
||||
hasTransaction?: T;
|
||||
throwAfterChange?: T;
|
||||
updatedAt?: T;
|
||||
createdAt?: T;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "default-values_select".
|
||||
*/
|
||||
export interface DefaultValuesSelect<T extends boolean = true> {
|
||||
title?: T;
|
||||
defaultValue?: T;
|
||||
array?:
|
||||
| T
|
||||
| {
|
||||
defaultValue?: T;
|
||||
id?: T;
|
||||
};
|
||||
group?:
|
||||
| T
|
||||
| {
|
||||
defaultValue?: T;
|
||||
};
|
||||
select?: T;
|
||||
updatedAt?: T;
|
||||
createdAt?: T;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "relation-a_select".
|
||||
*/
|
||||
export interface RelationASelect<T extends boolean = true> {
|
||||
title?: T;
|
||||
richText?: T;
|
||||
updatedAt?: T;
|
||||
createdAt?: T;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "relation-b_select".
|
||||
*/
|
||||
export interface RelationBSelect<T extends boolean = true> {
|
||||
title?: T;
|
||||
relationship?: T;
|
||||
richText?: T;
|
||||
updatedAt?: T;
|
||||
createdAt?: T;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "pg-migrations_select".
|
||||
*/
|
||||
export interface PgMigrationsSelect<T extends boolean = true> {
|
||||
relation1?: T;
|
||||
myArray?:
|
||||
| T
|
||||
| {
|
||||
relation2?: T;
|
||||
mySubArray?:
|
||||
| T
|
||||
| {
|
||||
relation3?: T;
|
||||
id?: T;
|
||||
};
|
||||
id?: T;
|
||||
};
|
||||
myGroup?:
|
||||
| T
|
||||
| {
|
||||
relation4?: T;
|
||||
};
|
||||
myBlocks?:
|
||||
| T
|
||||
| {
|
||||
myBlock?:
|
||||
| T
|
||||
| {
|
||||
relation5?: T;
|
||||
relation6?: T;
|
||||
id?: T;
|
||||
blockName?: T;
|
||||
};
|
||||
};
|
||||
updatedAt?: T;
|
||||
createdAt?: T;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "custom-schema_select".
|
||||
*/
|
||||
export interface CustomSchemaSelect<T extends boolean = true> {
|
||||
text?: T;
|
||||
localizedText?: T;
|
||||
relationship?: T;
|
||||
select?: T;
|
||||
radio?: T;
|
||||
array?:
|
||||
| T
|
||||
| {
|
||||
text?: T;
|
||||
localizedText?: T;
|
||||
id?: T;
|
||||
};
|
||||
blocks?:
|
||||
| T
|
||||
| {
|
||||
block?:
|
||||
| T
|
||||
| {
|
||||
text?: T;
|
||||
localizedText?: T;
|
||||
id?: T;
|
||||
blockName?: T;
|
||||
};
|
||||
};
|
||||
updatedAt?: T;
|
||||
createdAt?: T;
|
||||
_status?: T;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "places_select".
|
||||
*/
|
||||
export interface PlacesSelect<T extends boolean = true> {
|
||||
country?: T;
|
||||
city?: T;
|
||||
updatedAt?: T;
|
||||
createdAt?: T;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "fields-persistance_select".
|
||||
*/
|
||||
export interface FieldsPersistanceSelect<T extends boolean = true> {
|
||||
text?: T;
|
||||
textHooked?: T;
|
||||
array?:
|
||||
| T
|
||||
| {
|
||||
id?: T;
|
||||
};
|
||||
textWithinRow?: T;
|
||||
textWithinCollapsible?: T;
|
||||
textWithinTabs?: T;
|
||||
updatedAt?: T;
|
||||
createdAt?: T;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "custom-ids_select".
|
||||
*/
|
||||
export interface CustomIdsSelect<T extends boolean = true> {
|
||||
id?: T;
|
||||
updatedAt?: T;
|
||||
createdAt?: T;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "users_select".
|
||||
*/
|
||||
export interface UsersSelect<T extends boolean = true> {
|
||||
updatedAt?: T;
|
||||
createdAt?: T;
|
||||
email?: T;
|
||||
resetPasswordToken?: T;
|
||||
resetPasswordExpiration?: T;
|
||||
salt?: T;
|
||||
hash?: T;
|
||||
loginAttempts?: T;
|
||||
lockUntil?: T;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "payload-locked-documents_select".
|
||||
*/
|
||||
export interface PayloadLockedDocumentsSelect<T extends boolean = true> {
|
||||
document?: T;
|
||||
globalSlug?: T;
|
||||
user?: T;
|
||||
updatedAt?: T;
|
||||
createdAt?: T;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "payload-preferences_select".
|
||||
*/
|
||||
export interface PayloadPreferencesSelect<T extends boolean = true> {
|
||||
user?: T;
|
||||
key?: T;
|
||||
value?: T;
|
||||
updatedAt?: T;
|
||||
createdAt?: T;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "payload-migrations_select".
|
||||
*/
|
||||
export interface PayloadMigrationsSelect<T extends boolean = true> {
|
||||
name?: T;
|
||||
batch?: T;
|
||||
updatedAt?: T;
|
||||
createdAt?: T;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "global".
|
||||
@@ -344,6 +589,16 @@ export interface Global {
|
||||
updatedAt?: string | null;
|
||||
createdAt?: string | null;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "global_select".
|
||||
*/
|
||||
export interface GlobalSelect<T extends boolean = true> {
|
||||
text?: T;
|
||||
updatedAt?: T;
|
||||
createdAt?: T;
|
||||
globalType?: T;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "auth".
|
||||
|
||||
Reference in New Issue
Block a user