Skip to content

Commit

Permalink
fix(core): hydrate relations with mapToPk as scalars to support cus…
Browse files Browse the repository at this point in the history
…tom types

Closes #4803
  • Loading branch information
B4nan committed Oct 10, 2023
1 parent fba17f6 commit 4118076
Show file tree
Hide file tree
Showing 2 changed files with 123 additions and 10 deletions.
15 changes: 5 additions & 10 deletions packages/core/src/hydration/ObjectHydrator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -129,19 +129,15 @@ export class ObjectHydrator extends Hydrator {
ret.push(` } else if (typeof data${dataKey} !== 'undefined') {`);
ret.push(` if (isPrimaryKey(data${dataKey}, true)) {`);

if (prop.mapToPk) {
ret.push(` entity${entityKey} = data${dataKey};`);
} else if (prop.wrappedReference) {
if (prop.wrappedReference) {
ret.push(` entity${entityKey} = Reference.create(factory.createReference('${prop.type}', data${dataKey}, { merge: true, convertCustomTypes, schema }));`);
} else {
ret.push(` entity${entityKey} = factory.createReference('${prop.type}', data${dataKey}, { merge: true, convertCustomTypes, schema });`);
}

ret.push(` } else if (data${dataKey} && typeof data${dataKey} === 'object') {`);

if (prop.mapToPk) {
ret.push(` entity${entityKey} = data${dataKey};`);
} else if (prop.wrappedReference) {
if (prop.wrappedReference) {
ret.push(` entity${entityKey} = Reference.create(factory.create('${prop.type}', data${dataKey}, { initialized: true, merge: true, newEntity, convertCustomTypes, schema }));`);
} else {
ret.push(` entity${entityKey} = factory.create('${prop.type}', data${dataKey}, { initialized: true, merge: true, newEntity, convertCustomTypes, schema });`);
Expand All @@ -150,7 +146,7 @@ export class ObjectHydrator extends Hydrator {
ret.push(` }`);
ret.push(` }`);

if (prop.reference === ReferenceType.ONE_TO_ONE && !prop.mapToPk) {
if (prop.reference === ReferenceType.ONE_TO_ONE) {
const meta2 = this.metadata.get(prop.type);
const prop2 = meta2.properties[prop.inversedBy || prop.mappedBy];

Expand All @@ -165,8 +161,7 @@ export class ObjectHydrator extends Hydrator {
context.set(`convertToDatabaseValue_${this.safeKey(prop.name)}`, (val: any) => prop.customType.convertToDatabaseValue(val, this.platform, { mode: 'hydration' }));

ret.push(` if (data${dataKey} != null && convertCustomTypes) {`);
const pk = prop.mapToPk ? '' : '.__helper.getPrimaryKey()';
ret.push(` data${dataKey} = convertToDatabaseValue_${this.safeKey(prop.name)}(entity${entityKey}${pk});`);
ret.push(` data${dataKey} = convertToDatabaseValue_${this.safeKey(prop.name)}(entity${entityKey}.__helper.getPrimaryKey());`);
ret.push(` }`);
}

Expand Down Expand Up @@ -313,7 +308,7 @@ export class ObjectHydrator extends Hydrator {
dataKey = dataKey ?? (object ? entityKey : this.wrap(prop.name));
const ret: string[] = [];

if (prop.reference === ReferenceType.MANY_TO_ONE || prop.reference === ReferenceType.ONE_TO_ONE) {
if ([ReferenceType.MANY_TO_ONE, ReferenceType.ONE_TO_ONE].includes(prop.reference) && !prop.mapToPk) {
ret.push(...hydrateToOne(prop, dataKey, entityKey));
} else if (prop.reference === ReferenceType.ONE_TO_MANY || prop.reference === ReferenceType.MANY_TO_MANY) {
ret.push(...hydrateToMany(prop, dataKey, entityKey));
Expand Down
118 changes: 118 additions & 0 deletions tests/features/custom-types/GH4803.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
import { EntityProperty, Platform, Type, EntitySchema, Collection, BigIntType } from '@mikro-orm/core';
import { MikroORM } from '@mikro-orm/sqlite';

class User {

readonly id!: Id;
readonly email!: string;
readonly profiles = new Collection<Profile>(this);

}

class Profile {

readonly id!: string;
readonly user!: Id;

}

const profileSchema = new EntitySchema<Profile>({
class: Profile,
properties: {
id: {
type: BigIntType,
primary: true,
autoincrement: true,
},
user: {
entity: () => User,
reference: 'm:1',
inversedBy: 'profiles',
mapToPk: true,
},
},
});

class Id extends Type<Id | undefined, string> {

readonly value?: bigint;

constructor(value: bigint | number) {
super();
if (value) {
this.value = BigInt(value);
}
}

convertToDatabaseValue(value: Id): string {
return value?.value as unknown as string;
}

convertToJSValue(value: string): Id | undefined {
return value ? new Id(+value) : undefined;
}

getColumnType(prop: EntityProperty, platform: Platform) {
return platform.getBigIntTypeDeclarationSQL(prop);
}

compareAsType(): string {
return 'string';
}

}

const userSchema = new EntitySchema<User>({
class: User,
properties: {
id: {
type: Id,
primary: true,
autoincrement: true,
},
email: {
type: 'string',
},
profiles: {
entity: () => Profile,
reference: '1:m',
mappedBy: 'user',
nullable: true,
},
},
});

let orm: MikroORM;

beforeAll(async () => {
orm = await MikroORM.init({
dbName: ':memory:',
entities: [userSchema, profileSchema],
});

await orm.schema.createSchema();
});

afterAll(async () => {
await orm.close();
});

test('A profile user id should be a custom type', async () => {
const em = orm.em.fork();
const aUser = orm.em.create(User, {
email: 'user@mail.com',
});
await em.persistAndFlush(aUser);

const aProfile = orm.em.create(Profile, {
user: aUser.id,
});
await em.persistAndFlush(aProfile);

const userProfile = await em.findOneOrFail(Profile, { id: aProfile.id }, {
refresh: true,
});

expect(userProfile).toBeTruthy();
expect(userProfile.user).toBeInstanceOf(Id);
});

0 comments on commit 4118076

Please sign in to comment.