Skip to content

Commit

Permalink
feat(entity-generator): support mapToPk option (#5241)
Browse files Browse the repository at this point in the history
As a nice side effect, relations in metadata hooks now feature
information about the types of the columns involved in FKs.
  • Loading branch information
boenrobot committed Feb 12, 2024
1 parent 2193fd3 commit 3afaa29
Show file tree
Hide file tree
Showing 4 changed files with 32 additions and 10 deletions.
19 changes: 15 additions & 4 deletions packages/entity-generator/src/SourceFile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -190,18 +190,25 @@ export class SourceFile {
return `${padding}${prop.name}${hiddenType ? `: Collection<${prop.type}>${hiddenType}` : ''} = new Collection<${prop.type}>(this);\n`;
}

const propType = prop.mapToPk
? (() => {
const runtimeTypes = prop.columnTypes.map(t => this.platform.getMappedType(t).runtimeType);
return runtimeTypes.length === 1 ? runtimeTypes[0] : this.serializeObject(runtimeTypes);
})()
: prop.type;

// string defaults are usually things like SQL functions, but can be also enums, for that `useDefault` should be true
const isEnumOrNonStringDefault = prop.enum || typeof prop.default !== 'string';
const useDefault = prop.default != null && isEnumOrNonStringDefault;
const optional = prop.nullable ? '?' : (useDefault ? '' : '!');

if (prop.ref) {
this.coreImports.add('Ref');
this.entityImports.add(prop.type);
return `${padding}${prop.name}${optional}: Ref<${prop.type}>${hiddenType};\n`;
this.entityImports.add(propType);
return `${padding}${prop.name}${optional}: Ref<${propType}>${hiddenType};\n`;
}

let ret = `${prop.name}${optional}: ${prop.type}`;
let ret = `${prop.name}${optional}: ${propType}`;

if (prop.kind === ReferenceKind.EMBEDDED && prop.array) {
ret += '[]';
Expand All @@ -218,7 +225,7 @@ export class SourceFile {
}

if (prop.enum && typeof prop.default === 'string') {
return `${padding}${ret} = ${prop.type}.${prop.default.toUpperCase()};\n`;
return `${padding}${ret} = ${propType}.${prop.default.toUpperCase()};\n`;
}

return `${padding}${ret} = ${prop.default};\n`;
Expand Down Expand Up @@ -572,6 +579,10 @@ export class SourceFile {
options.ref = true;
}

if (prop.mapToPk) {
options.mapToPk = true;
}

if (prop.mappedBy) {
options.mappedBy = this.quote(prop.mappedBy);
return;
Expand Down
1 change: 1 addition & 0 deletions packages/knex/src/schema/DatabaseTable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -599,6 +599,7 @@ export class DatabaseTable {
fkOptions.referencedColumnNames = fk.referencedColumnNames;
fkOptions.updateRule = fk.updateRule?.toLowerCase();
fkOptions.deleteRule = fk.deleteRule?.toLowerCase();
fkOptions.columnTypes = fk.columnNames.map(c => this.getColumn(c)!.type);

const columnOptions: Partial<EntityProperty> = {};
if (fk.columnNames.length === 1) {
Expand Down
8 changes: 8 additions & 0 deletions tests/features/entity-generator/MetadataHooks.mysql.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,14 @@ const processedMetadataProcessor: GenerateOptions['onProcessedMetadata'] = (meta
expect(entity.embeddable).toBeFalsy();
}

if (entity.className === 'Book2') {
entity.properties.publisher.mapToPk = true;
}

if (entity.className === 'User2') {
entity.properties.favouriteCar.mapToPk = true;
}

if (entity.className === 'Author2') {
const authorToFriend = entity.properties.authorToFriend;
expect(authorToFriend.kind).toBe(ReferenceKind.MANY_TO_MANY);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -207,8 +207,8 @@ export class Book2 {
@ManyToOne({ entity: () => Author2 })
author!: Author2;
@ManyToOne({ entity: () => Publisher2, updateRule: 'cascade', deleteRule: 'cascade', nullable: true })
publisher?: Publisher2;
@ManyToOne({ entity: () => Publisher2, mapToPk: true, updateRule: 'cascade', deleteRule: 'cascade', nullable: true })
publisher?: number;
@Property({ length: 255, nullable: true, default: 'lol' })
foo?: string;
Expand Down Expand Up @@ -578,8 +578,8 @@ export class User2 {
@Property({ nullable: true })
foo?: number;
@OneToOne({ entity: () => Car2, updateRule: 'cascade', deleteRule: 'set null', nullable: true })
favouriteCar?: Car2;
@OneToOne({ entity: () => Car2, mapToPk: true, updateRule: 'cascade', deleteRule: 'set null', nullable: true })
favouriteCar?: [string, number];
@ManyToMany({ entity: () => Car2, joinColumns: ['user2_first_name', 'user2_last_name'], inverseJoinColumns: ['car2_name', 'car2_year'] })
cars = new Collection<Car2>(this);
Expand Down Expand Up @@ -895,7 +895,7 @@ export class Book2 {
double?: number;
meta?: any;
author!: Author2;
publisher?: Publisher2;
publisher?: number;
foo?: string;
bookToTagUnordered = new Collection<BookTag2>(this);
favouriteBookInverse = new Collection<Author2>(this);
Expand All @@ -917,6 +917,7 @@ export const Book2Schema = new EntitySchema({
publisher: {
kind: 'm:1',
entity: () => Publisher2,
mapToPk: true,
updateRule: 'cascade',
deleteRule: 'cascade',
nullable: true,
Expand Down Expand Up @@ -1295,7 +1296,7 @@ export class User2 {
firstName!: string;
lastName!: string;
foo?: number;
favouriteCar?: Car2;
favouriteCar?: [string, number];
cars = new Collection<Car2>(this);
sandwiches = new Collection<Sandwich>(this);
}
Expand All @@ -1309,6 +1310,7 @@ export const User2Schema = new EntitySchema({
favouriteCar: {
kind: '1:1',
entity: () => Car2,
mapToPk: true,
updateRule: 'cascade',
deleteRule: 'set null',
nullable: true,
Expand Down

0 comments on commit 3afaa29

Please sign in to comment.