Add support for pivot table and entity in manyToMany relationships (#7779)
This commit is contained in:
1 parent
79a8f0ef2c
commit
45ad70e96b
9 files changed
+512
-25
No files matched your search
@@ -22,6 +22,7 @@ describe("Base relationship", () => {
|
|||||||
name: "user",
|
name: "user",
|
||||||
type: "hasOne",
|
type: "hasOne",
|
||||||
nullable: false,
|
nullable: false,
|
||||||
|
options: { mappedBy: "user_id" },
|
||||||
mappedBy: "user_id",
|
mappedBy: "user_id",
|
||||||
entity: entityRef,
|
entity: entityRef,
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -3023,7 +3023,7 @@ describe("Entity builder", () => {
|
|||||||
const user = model.define("user", {
|
const user = model.define("user", {
|
||||||
id: model.number(),
|
id: model.number(),
|
||||||
username: model.text(),
|
username: model.text(),
|
||||||
email: model.manyToMany(() => email),
|
email: model.belongsTo(() => email),
|
||||||
})
|
})
|
||||||
|
|
||||||
const entityBuilder = createMikrORMEntity()
|
const entityBuilder = createMikrORMEntity()
|
||||||
@@ -4526,5 +4526,435 @@ describe("Entity builder", () => {
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test("define custom pivot table name", () => {
|
||||||
|
const model = new EntityBuilder()
|
||||||
|
const team = model.define("team", {
|
||||||
|
id: model.number(),
|
||||||
|
name: model.text(),
|
||||||
|
users: model.manyToMany(() => user, {
|
||||||
|
pivotTable: "users_teams",
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
|
||||||
|
const user = model.define("user", {
|
||||||
|
id: model.number(),
|
||||||
|
username: model.text(),
|
||||||
|
teams: model.manyToMany(() => team, { pivotTable: "users_teams" }),
|
||||||
|
})
|
||||||
|
|
||||||
|
const entityBuilder = createMikrORMEntity()
|
||||||
|
const User = entityBuilder(user)
|
||||||
|
const Team = entityBuilder(team)
|
||||||
|
|
||||||
|
expectTypeOf(new User()).toMatchTypeOf<{
|
||||||
|
id: number
|
||||||
|
username: string
|
||||||
|
teams: EntityConstructor<{
|
||||||
|
id: number
|
||||||
|
name: string
|
||||||
|
users: EntityConstructor<{
|
||||||
|
id: number
|
||||||
|
username: string
|
||||||
|
}>
|
||||||
|
}>
|
||||||
|
}>()
|
||||||
|
|
||||||
|
expectTypeOf(new Team()).toMatchTypeOf<{
|
||||||
|
id: number
|
||||||
|
name: string
|
||||||
|
users: EntityConstructor<{
|
||||||
|
id: number
|
||||||
|
username: string
|
||||||
|
teams: EntityConstructor<{
|
||||||
|
id: number
|
||||||
|
name: string
|
||||||
|
}>
|
||||||
|
}>
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const metaData = MetadataStorage.getMetadataFromDecorator(User)
|
||||||
|
expect(metaData.className).toEqual("User")
|
||||||
|
expect(metaData.path).toEqual("User")
|
||||||
|
expect(metaData.properties).toEqual({
|
||||||
|
id: {
|
||||||
|
reference: "scalar",
|
||||||
|
type: "number",
|
||||||
|
columnType: "integer",
|
||||||
|
name: "id",
|
||||||
|
nullable: false,
|
||||||
|
getter: false,
|
||||||
|
setter: false,
|
||||||
|
},
|
||||||
|
username: {
|
||||||
|
reference: "scalar",
|
||||||
|
type: "string",
|
||||||
|
columnType: "text",
|
||||||
|
name: "username",
|
||||||
|
nullable: false,
|
||||||
|
getter: false,
|
||||||
|
setter: false,
|
||||||
|
},
|
||||||
|
teams: {
|
||||||
|
reference: "m:n",
|
||||||
|
name: "teams",
|
||||||
|
entity: "Team",
|
||||||
|
pivotTable: "users_teams",
|
||||||
|
},
|
||||||
|
created_at: {
|
||||||
|
reference: "scalar",
|
||||||
|
type: "date",
|
||||||
|
columnType: "timestamptz",
|
||||||
|
name: "created_at",
|
||||||
|
defaultRaw: "now()",
|
||||||
|
onCreate: expect.any(Function),
|
||||||
|
nullable: false,
|
||||||
|
getter: false,
|
||||||
|
setter: false,
|
||||||
|
},
|
||||||
|
updated_at: {
|
||||||
|
reference: "scalar",
|
||||||
|
type: "date",
|
||||||
|
columnType: "timestamptz",
|
||||||
|
name: "updated_at",
|
||||||
|
defaultRaw: "now()",
|
||||||
|
onCreate: expect.any(Function),
|
||||||
|
onUpdate: expect.any(Function),
|
||||||
|
nullable: false,
|
||||||
|
getter: false,
|
||||||
|
setter: false,
|
||||||
|
},
|
||||||
|
deleted_at: {
|
||||||
|
reference: "scalar",
|
||||||
|
type: "date",
|
||||||
|
columnType: "timestamptz",
|
||||||
|
name: "deleted_at",
|
||||||
|
nullable: true,
|
||||||
|
getter: false,
|
||||||
|
setter: false,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const teamMetaData = MetadataStorage.getMetadataFromDecorator(Team)
|
||||||
|
expect(teamMetaData.className).toEqual("Team")
|
||||||
|
expect(teamMetaData.path).toEqual("Team")
|
||||||
|
expect(teamMetaData.properties).toEqual({
|
||||||
|
id: {
|
||||||
|
reference: "scalar",
|
||||||
|
type: "number",
|
||||||
|
columnType: "integer",
|
||||||
|
name: "id",
|
||||||
|
nullable: false,
|
||||||
|
getter: false,
|
||||||
|
setter: false,
|
||||||
|
},
|
||||||
|
name: {
|
||||||
|
reference: "scalar",
|
||||||
|
type: "string",
|
||||||
|
columnType: "text",
|
||||||
|
name: "name",
|
||||||
|
nullable: false,
|
||||||
|
getter: false,
|
||||||
|
setter: false,
|
||||||
|
},
|
||||||
|
users: {
|
||||||
|
reference: "m:n",
|
||||||
|
name: "users",
|
||||||
|
entity: "User",
|
||||||
|
pivotTable: "users_teams",
|
||||||
|
},
|
||||||
|
created_at: {
|
||||||
|
reference: "scalar",
|
||||||
|
type: "date",
|
||||||
|
columnType: "timestamptz",
|
||||||
|
name: "created_at",
|
||||||
|
defaultRaw: "now()",
|
||||||
|
onCreate: expect.any(Function),
|
||||||
|
nullable: false,
|
||||||
|
getter: false,
|
||||||
|
setter: false,
|
||||||
|
},
|
||||||
|
updated_at: {
|
||||||
|
reference: "scalar",
|
||||||
|
type: "date",
|
||||||
|
columnType: "timestamptz",
|
||||||
|
name: "updated_at",
|
||||||
|
defaultRaw: "now()",
|
||||||
|
onCreate: expect.any(Function),
|
||||||
|
onUpdate: expect.any(Function),
|
||||||
|
nullable: false,
|
||||||
|
getter: false,
|
||||||
|
setter: false,
|
||||||
|
},
|
||||||
|
deleted_at: {
|
||||||
|
reference: "scalar",
|
||||||
|
type: "date",
|
||||||
|
columnType: "timestamptz",
|
||||||
|
name: "deleted_at",
|
||||||
|
nullable: true,
|
||||||
|
getter: false,
|
||||||
|
setter: false,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
test("define custom pivot entity", () => {
|
||||||
|
const model = new EntityBuilder()
|
||||||
|
const team = model.define("team", {
|
||||||
|
id: model.number(),
|
||||||
|
name: model.text(),
|
||||||
|
users: model.manyToMany(() => user, {
|
||||||
|
pivotEntity: () => squad,
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
|
||||||
|
const squad = model.define("teamUsers", {
|
||||||
|
id: model.number(),
|
||||||
|
user: model.belongsTo(() => user, { mappedBy: "teams" }),
|
||||||
|
team: model.belongsTo(() => team, { mappedBy: "users" }),
|
||||||
|
})
|
||||||
|
|
||||||
|
const user = model.define("user", {
|
||||||
|
id: model.number(),
|
||||||
|
username: model.text(),
|
||||||
|
teams: model.manyToMany(() => team, { pivotEntity: () => squad }),
|
||||||
|
})
|
||||||
|
|
||||||
|
const entityBuilder = createMikrORMEntity()
|
||||||
|
const User = entityBuilder(user)
|
||||||
|
const Team = entityBuilder(team)
|
||||||
|
const Squad = entityBuilder(squad)
|
||||||
|
|
||||||
|
expectTypeOf(new User()).toMatchTypeOf<{
|
||||||
|
id: number
|
||||||
|
username: string
|
||||||
|
teams: EntityConstructor<{
|
||||||
|
id: number
|
||||||
|
name: string
|
||||||
|
users: EntityConstructor<{
|
||||||
|
id: number
|
||||||
|
username: string
|
||||||
|
}>
|
||||||
|
}>
|
||||||
|
}>()
|
||||||
|
|
||||||
|
expectTypeOf(new Team()).toMatchTypeOf<{
|
||||||
|
id: number
|
||||||
|
name: string
|
||||||
|
users: EntityConstructor<{
|
||||||
|
id: number
|
||||||
|
username: string
|
||||||
|
teams: EntityConstructor<{
|
||||||
|
id: number
|
||||||
|
name: string
|
||||||
|
}>
|
||||||
|
}>
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const squadMetaData = MetadataStorage.getMetadataFromDecorator(Squad)
|
||||||
|
expect(squadMetaData.className).toEqual("TeamUsers")
|
||||||
|
expect(squadMetaData.path).toEqual("TeamUsers")
|
||||||
|
expect(squadMetaData.tableName).toEqual("team_users")
|
||||||
|
|
||||||
|
expect(squadMetaData.properties).toEqual({
|
||||||
|
id: {
|
||||||
|
reference: "scalar",
|
||||||
|
type: "number",
|
||||||
|
columnType: "integer",
|
||||||
|
name: "id",
|
||||||
|
nullable: false,
|
||||||
|
getter: false,
|
||||||
|
setter: false,
|
||||||
|
},
|
||||||
|
team: {
|
||||||
|
entity: "Team",
|
||||||
|
name: "team",
|
||||||
|
persist: false,
|
||||||
|
reference: "m:1",
|
||||||
|
},
|
||||||
|
team_id: {
|
||||||
|
columnType: "text",
|
||||||
|
entity: "Team",
|
||||||
|
fieldName: "team_id",
|
||||||
|
mapToPk: true,
|
||||||
|
name: "team_id",
|
||||||
|
nullable: false,
|
||||||
|
onDelete: undefined,
|
||||||
|
reference: "m:1",
|
||||||
|
},
|
||||||
|
user: {
|
||||||
|
entity: "User",
|
||||||
|
name: "user",
|
||||||
|
persist: false,
|
||||||
|
reference: "m:1",
|
||||||
|
},
|
||||||
|
user_id: {
|
||||||
|
columnType: "text",
|
||||||
|
entity: "User",
|
||||||
|
fieldName: "user_id",
|
||||||
|
mapToPk: true,
|
||||||
|
name: "user_id",
|
||||||
|
nullable: false,
|
||||||
|
onDelete: undefined,
|
||||||
|
reference: "m:1",
|
||||||
|
},
|
||||||
|
created_at: {
|
||||||
|
reference: "scalar",
|
||||||
|
type: "date",
|
||||||
|
columnType: "timestamptz",
|
||||||
|
name: "created_at",
|
||||||
|
defaultRaw: "now()",
|
||||||
|
onCreate: expect.any(Function),
|
||||||
|
nullable: false,
|
||||||
|
getter: false,
|
||||||
|
setter: false,
|
||||||
|
},
|
||||||
|
updated_at: {
|
||||||
|
reference: "scalar",
|
||||||
|
type: "date",
|
||||||
|
columnType: "timestamptz",
|
||||||
|
name: "updated_at",
|
||||||
|
defaultRaw: "now()",
|
||||||
|
onCreate: expect.any(Function),
|
||||||
|
onUpdate: expect.any(Function),
|
||||||
|
nullable: false,
|
||||||
|
getter: false,
|
||||||
|
setter: false,
|
||||||
|
},
|
||||||
|
deleted_at: {
|
||||||
|
reference: "scalar",
|
||||||
|
type: "date",
|
||||||
|
columnType: "timestamptz",
|
||||||
|
name: "deleted_at",
|
||||||
|
nullable: true,
|
||||||
|
getter: false,
|
||||||
|
setter: false,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const metaData = MetadataStorage.getMetadataFromDecorator(User)
|
||||||
|
expect(metaData.className).toEqual("User")
|
||||||
|
expect(metaData.path).toEqual("User")
|
||||||
|
expect(metaData.properties).toEqual({
|
||||||
|
id: {
|
||||||
|
reference: "scalar",
|
||||||
|
type: "number",
|
||||||
|
columnType: "integer",
|
||||||
|
name: "id",
|
||||||
|
nullable: false,
|
||||||
|
getter: false,
|
||||||
|
setter: false,
|
||||||
|
},
|
||||||
|
username: {
|
||||||
|
reference: "scalar",
|
||||||
|
type: "string",
|
||||||
|
columnType: "text",
|
||||||
|
name: "username",
|
||||||
|
nullable: false,
|
||||||
|
getter: false,
|
||||||
|
setter: false,
|
||||||
|
},
|
||||||
|
teams: {
|
||||||
|
reference: "m:n",
|
||||||
|
name: "teams",
|
||||||
|
entity: "Team",
|
||||||
|
pivotEntity: "TeamUsers",
|
||||||
|
},
|
||||||
|
created_at: {
|
||||||
|
reference: "scalar",
|
||||||
|
type: "date",
|
||||||
|
columnType: "timestamptz",
|
||||||
|
name: "created_at",
|
||||||
|
defaultRaw: "now()",
|
||||||
|
onCreate: expect.any(Function),
|
||||||
|
nullable: false,
|
||||||
|
getter: false,
|
||||||
|
setter: false,
|
||||||
|
},
|
||||||
|
updated_at: {
|
||||||
|
reference: "scalar",
|
||||||
|
type: "date",
|
||||||
|
columnType: "timestamptz",
|
||||||
|
name: "updated_at",
|
||||||
|
defaultRaw: "now()",
|
||||||
|
onCreate: expect.any(Function),
|
||||||
|
onUpdate: expect.any(Function),
|
||||||
|
nullable: false,
|
||||||
|
getter: false,
|
||||||
|
setter: false,
|
||||||
|
},
|
||||||
|
deleted_at: {
|
||||||
|
reference: "scalar",
|
||||||
|
type: "date",
|
||||||
|
columnType: "timestamptz",
|
||||||
|
name: "deleted_at",
|
||||||
|
nullable: true,
|
||||||
|
getter: false,
|
||||||
|
setter: false,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const teamMetaData = MetadataStorage.getMetadataFromDecorator(Team)
|
||||||
|
expect(teamMetaData.className).toEqual("Team")
|
||||||
|
expect(teamMetaData.path).toEqual("Team")
|
||||||
|
expect(teamMetaData.properties).toEqual({
|
||||||
|
id: {
|
||||||
|
reference: "scalar",
|
||||||
|
type: "number",
|
||||||
|
columnType: "integer",
|
||||||
|
name: "id",
|
||||||
|
nullable: false,
|
||||||
|
getter: false,
|
||||||
|
setter: false,
|
||||||
|
},
|
||||||
|
name: {
|
||||||
|
reference: "scalar",
|
||||||
|
type: "string",
|
||||||
|
columnType: "text",
|
||||||
|
name: "name",
|
||||||
|
nullable: false,
|
||||||
|
getter: false,
|
||||||
|
setter: false,
|
||||||
|
},
|
||||||
|
users: {
|
||||||
|
reference: "m:n",
|
||||||
|
name: "users",
|
||||||
|
entity: "User",
|
||||||
|
pivotEntity: "TeamUsers",
|
||||||
|
},
|
||||||
|
created_at: {
|
||||||
|
reference: "scalar",
|
||||||
|
type: "date",
|
||||||
|
columnType: "timestamptz",
|
||||||
|
name: "created_at",
|
||||||
|
defaultRaw: "now()",
|
||||||
|
onCreate: expect.any(Function),
|
||||||
|
nullable: false,
|
||||||
|
getter: false,
|
||||||
|
setter: false,
|
||||||
|
},
|
||||||
|
updated_at: {
|
||||||
|
reference: "scalar",
|
||||||
|
type: "date",
|
||||||
|
columnType: "timestamptz",
|
||||||
|
name: "updated_at",
|
||||||
|
defaultRaw: "now()",
|
||||||
|
onCreate: expect.any(Function),
|
||||||
|
onUpdate: expect.any(Function),
|
||||||
|
nullable: false,
|
||||||
|
getter: false,
|
||||||
|
setter: false,
|
||||||
|
},
|
||||||
|
deleted_at: {
|
||||||
|
reference: "scalar",
|
||||||
|
type: "date",
|
||||||
|
columnType: "timestamptz",
|
||||||
|
name: "deleted_at",
|
||||||
|
nullable: true,
|
||||||
|
getter: false,
|
||||||
|
setter: false,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@@ -16,6 +16,7 @@ describe("HasMany relationship", () => {
|
|||||||
name: "user",
|
name: "user",
|
||||||
type: "hasMany",
|
type: "hasMany",
|
||||||
nullable: false,
|
nullable: false,
|
||||||
|
options: {},
|
||||||
entity: entityRef,
|
entity: entityRef,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ describe("HasOne relationship", () => {
|
|||||||
name: "user",
|
name: "user",
|
||||||
type: "hasOne",
|
type: "hasOne",
|
||||||
nullable: false,
|
nullable: false,
|
||||||
|
options: {},
|
||||||
entity: entityRef,
|
entity: entityRef,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@@ -35,6 +36,7 @@ describe("HasOne relationship", () => {
|
|||||||
name: "user",
|
name: "user",
|
||||||
type: "hasOne",
|
type: "hasOne",
|
||||||
nullable: true,
|
nullable: true,
|
||||||
|
options: {},
|
||||||
entity: entityRef,
|
entity: entityRef,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ describe("ManyToMany relationship", () => {
|
|||||||
name: "user",
|
name: "user",
|
||||||
type: "manyToMany",
|
type: "manyToMany",
|
||||||
nullable: false,
|
nullable: false,
|
||||||
|
options: {},
|
||||||
entity: entityRef,
|
entity: entityRef,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ export class EntityBuilder {
|
|||||||
* Define an id property. Id properties are marked
|
* Define an id property. Id properties are marked
|
||||||
* primary by default
|
* primary by default
|
||||||
*/
|
*/
|
||||||
id(options: ConstructorParameters<typeof IdProperty>[0]) {
|
id(options?: ConstructorParameters<typeof IdProperty>[0]) {
|
||||||
return new IdProperty(options)
|
return new IdProperty(options)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -162,7 +162,20 @@ export class EntityBuilder {
|
|||||||
* relationship requires a pivot table to establish a many to many
|
* relationship requires a pivot table to establish a many to many
|
||||||
* relationship between two entities
|
* relationship between two entities
|
||||||
*/
|
*/
|
||||||
manyToMany<T>(entityBuilder: T, options?: RelationshipOptions) {
|
manyToMany<T>(
|
||||||
|
entityBuilder: T,
|
||||||
|
options?: RelationshipOptions &
|
||||||
|
(
|
||||||
|
| {
|
||||||
|
pivotTable?: string
|
||||||
|
pivotEntity?: never
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
pivotTable?: never
|
||||||
|
pivotEntity?: () => DmlEntity<any>
|
||||||
|
}
|
||||||
|
)
|
||||||
|
) {
|
||||||
return new ManyToMany<T>(entityBuilder, options || {})
|
return new ManyToMany<T>(entityBuilder, options || {})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -327,7 +327,10 @@ export function createMikrORMEntity() {
|
|||||||
/**
|
/**
|
||||||
* Otherside is a has many. Hence we should defined a ManyToOne
|
* Otherside is a has many. Hence we should defined a ManyToOne
|
||||||
*/
|
*/
|
||||||
if (otherSideRelation instanceof HasMany) {
|
if (
|
||||||
|
otherSideRelation instanceof HasMany ||
|
||||||
|
otherSideRelation instanceof DmlManyToMany
|
||||||
|
) {
|
||||||
ManyToOne({
|
ManyToOne({
|
||||||
entity: relatedModelName,
|
entity: relatedModelName,
|
||||||
columnType: "text",
|
columnType: "text",
|
||||||
@@ -382,28 +385,12 @@ export function createMikrORMEntity() {
|
|||||||
) {
|
) {
|
||||||
let mappedBy = relationship.mappedBy
|
let mappedBy = relationship.mappedBy
|
||||||
let inversedBy: undefined | string
|
let inversedBy: undefined | string
|
||||||
|
let pivotEntityName: undefined | string
|
||||||
|
let pivotTableName: undefined | string
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A consistent pivot table name is created by:
|
* Validating other side of relationship when mapped by is defined
|
||||||
*
|
|
||||||
* - Combining both the entity's names.
|
|
||||||
* - Sorting them by alphabetical order
|
|
||||||
* - Converting them from camelCase to snake_case.
|
|
||||||
* - And finally pluralizing the second entity name.
|
|
||||||
*/
|
*/
|
||||||
const pivotTableName = [
|
|
||||||
MikroORMEntity.name.toLowerCase(),
|
|
||||||
relatedModelName.toLowerCase(),
|
|
||||||
]
|
|
||||||
.sort()
|
|
||||||
.map((token, index) => {
|
|
||||||
if (index === 1) {
|
|
||||||
return pluralize(camelToSnakeCase(token))
|
|
||||||
}
|
|
||||||
return camelToSnakeCase(token)
|
|
||||||
})
|
|
||||||
.join("_")
|
|
||||||
|
|
||||||
if (mappedBy) {
|
if (mappedBy) {
|
||||||
const otherSideRelation = relatedEntity.parse().schema[mappedBy]
|
const otherSideRelation = relatedEntity.parse().schema[mappedBy]
|
||||||
if (!otherSideRelation) {
|
if (!otherSideRelation) {
|
||||||
@@ -438,9 +425,59 @@ export function createMikrORMEntity() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validating pivot entity when it is defined and computing
|
||||||
|
* its name
|
||||||
|
*/
|
||||||
|
if (relationship.options.pivotEntity) {
|
||||||
|
if (typeof relationship.options.pivotEntity !== "function") {
|
||||||
|
throw new Error(
|
||||||
|
`Invalid pivotEntity reference for "${MikroORMEntity.name}.${relationship.name}". Make sure to define the pivotEntity using a factory function`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const pivotEntity = relationship.options.pivotEntity()
|
||||||
|
if (!(pivotEntity instanceof DmlEntity)) {
|
||||||
|
throw new Error(
|
||||||
|
`Invalid pivotEntity reference for "${MikroORMEntity.name}.${relationship.name}". Make sure to return a DML entity from the pivotEntity callback`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pivotEntityName = parseEntityName(pivotEntity.parse().name).modelName
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!pivotEntityName) {
|
||||||
|
/**
|
||||||
|
* Pivot table name is created as follows (when not explicitly provided)
|
||||||
|
*
|
||||||
|
* - Combining both the entity's names.
|
||||||
|
* - Sorting them by alphabetical order
|
||||||
|
* - Converting them from camelCase to snake_case.
|
||||||
|
* - And finally pluralizing the second entity name.
|
||||||
|
*/
|
||||||
|
pivotTableName =
|
||||||
|
relationship.options.pivotTable ??
|
||||||
|
[MikroORMEntity.name.toLowerCase(), relatedModelName.toLowerCase()]
|
||||||
|
.sort()
|
||||||
|
.map((token, index) => {
|
||||||
|
if (index === 1) {
|
||||||
|
return pluralize(camelToSnakeCase(token))
|
||||||
|
}
|
||||||
|
return camelToSnakeCase(token)
|
||||||
|
})
|
||||||
|
.join("_")
|
||||||
|
}
|
||||||
|
|
||||||
ManyToMany({
|
ManyToMany({
|
||||||
entity: relatedModelName,
|
entity: relatedModelName,
|
||||||
pivotTable: pgSchema ? `${pgSchema}.${pivotTableName}` : pivotTableName,
|
...(pivotTableName
|
||||||
|
? {
|
||||||
|
pivotTable: pgSchema
|
||||||
|
? `${pgSchema}.${pivotTableName}`
|
||||||
|
: pivotTableName,
|
||||||
|
}
|
||||||
|
: {}),
|
||||||
|
...(pivotEntityName ? { pivotEntity: pivotEntityName } : {}),
|
||||||
...(mappedBy ? { mappedBy: mappedBy as any } : {}),
|
...(mappedBy ? { mappedBy: mappedBy as any } : {}),
|
||||||
...(inversedBy ? { inversedBy: inversedBy as any } : {}),
|
...(inversedBy ? { inversedBy: inversedBy as any } : {}),
|
||||||
})(MikroORMEntity.prototype, relationship.name)
|
})(MikroORMEntity.prototype, relationship.name)
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ export abstract class BaseRelationship<T> implements RelationshipType<T> {
|
|||||||
name: relationshipName,
|
name: relationshipName,
|
||||||
nullable: false,
|
nullable: false,
|
||||||
mappedBy: this.options.mappedBy,
|
mappedBy: this.options.mappedBy,
|
||||||
|
options: this.options,
|
||||||
entity: this.#referencedEntity,
|
entity: this.#referencedEntity,
|
||||||
type: this.type,
|
type: this.type,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ export type PropertyType<T> = {
|
|||||||
*/
|
*/
|
||||||
export type RelationshipOptions = {
|
export type RelationshipOptions = {
|
||||||
mappedBy?: string
|
mappedBy?: string
|
||||||
}
|
} & Record<string, any>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The meta-data returned by the relationship parse
|
* The meta-data returned by the relationship parse
|
||||||
@@ -66,6 +66,7 @@ export type RelationshipMetadata = {
|
|||||||
entity: unknown
|
entity: unknown
|
||||||
nullable: boolean
|
nullable: boolean
|
||||||
mappedBy?: string
|
mappedBy?: string
|
||||||
|
options: Record<string, any>
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Reference in new issue
Block a user