chore(utils): DML#hasOne allow mappedBy to not be defined (#10442)
RESOLVES FRMW-2826 **What** We have many places where we define only one to one on one side of the relation. In that case it is a one to one that does not mapped by to the other side relation since it is not defined. This pr allow to create a one to one without mapped by. In order to remove breaking changes, in that case we ask the user to explicitly define the `mappedBy` as `undefined`
This commit is contained in:
committed by
GitHub
parent
223bcff379
commit
90d7f4ff39
5
.changeset/good-moles-camp.md
Normal file
5
.changeset/good-moles-camp.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"@medusajs/utils": patch
|
||||
---
|
||||
|
||||
chore(utils): DML#hasOne allow mappedBy to not be defined
|
||||
@@ -2461,6 +2461,93 @@ describe("Entity builder", () => {
|
||||
})
|
||||
})
|
||||
|
||||
test("define custom mappedBy key to undefined to not get the auto generated value", () => {
|
||||
const email = model.define("email", {
|
||||
email: model.text(),
|
||||
isVerified: model.boolean(),
|
||||
})
|
||||
|
||||
const user = model.define("user", {
|
||||
id: model.number(),
|
||||
username: model.text(),
|
||||
email: model.hasOne(() => email, { mappedBy: undefined }),
|
||||
})
|
||||
|
||||
const User = toMikroORMEntity(user)
|
||||
expectTypeOf(new User()).toMatchTypeOf<{
|
||||
id: number
|
||||
username: string
|
||||
email: { email: string; isVerified: boolean }
|
||||
}>()
|
||||
|
||||
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",
|
||||
fieldName: "id",
|
||||
nullable: false,
|
||||
getter: false,
|
||||
setter: false,
|
||||
},
|
||||
username: {
|
||||
reference: "scalar",
|
||||
type: "string",
|
||||
columnType: "text",
|
||||
name: "username",
|
||||
fieldName: "username",
|
||||
nullable: false,
|
||||
getter: false,
|
||||
setter: false,
|
||||
},
|
||||
email: {
|
||||
reference: "1:1",
|
||||
name: "email",
|
||||
entity: "Email",
|
||||
nullable: false,
|
||||
},
|
||||
created_at: {
|
||||
reference: "scalar",
|
||||
type: "date",
|
||||
columnType: "timestamptz",
|
||||
name: "created_at",
|
||||
fieldName: "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",
|
||||
fieldName: "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",
|
||||
fieldName: "deleted_at",
|
||||
nullable: true,
|
||||
getter: false,
|
||||
setter: false,
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
test("define custom mappedBy key for relationship", () => {
|
||||
const email = model.define("email", {
|
||||
email: model.text(),
|
||||
@@ -2899,6 +2986,7 @@ describe("Entity builder", () => {
|
||||
username: model.text(),
|
||||
email: model.hasOne(() => email, {
|
||||
foreignKey: true,
|
||||
mappedBy: undefined,
|
||||
}),
|
||||
})
|
||||
|
||||
@@ -3010,6 +3098,7 @@ describe("Entity builder", () => {
|
||||
emails: model
|
||||
.hasOne(() => email, {
|
||||
foreignKey: true,
|
||||
mappedBy: undefined,
|
||||
})
|
||||
.nullable(),
|
||||
})
|
||||
@@ -3265,6 +3354,7 @@ describe("Entity builder", () => {
|
||||
entity: "Email",
|
||||
nullable: false,
|
||||
cascade: ["persist", "soft-remove"],
|
||||
mappedBy: "user",
|
||||
},
|
||||
email_id: {
|
||||
columnType: "text",
|
||||
@@ -3440,6 +3530,7 @@ describe("Entity builder", () => {
|
||||
entity: "Email",
|
||||
nullable: false,
|
||||
cascade: ["persist", "soft-remove"],
|
||||
mappedBy: "user",
|
||||
},
|
||||
email_id: {
|
||||
columnType: "text",
|
||||
|
||||
@@ -11,17 +11,18 @@ import {
|
||||
ManyToOne,
|
||||
OneToMany,
|
||||
OneToOne,
|
||||
OneToOneOptions,
|
||||
OnInit,
|
||||
Property,
|
||||
rel,
|
||||
} from "@mikro-orm/core"
|
||||
import { DmlEntity } from "../../entity"
|
||||
import { HasOne } from "../../relations/has-one"
|
||||
import { HasMany } from "../../relations/has-many"
|
||||
import { parseEntityName } from "./parse-entity-name"
|
||||
import { camelToSnakeCase, pluralize } from "../../../common"
|
||||
import { applyEntityIndexes } from "../mikro-orm/apply-indexes"
|
||||
import { DmlEntity } from "../../entity"
|
||||
import { HasMany } from "../../relations/has-many"
|
||||
import { HasOne } from "../../relations/has-one"
|
||||
import { ManyToMany as DmlManyToMany } from "../../relations/many-to-many"
|
||||
import { applyEntityIndexes } from "../mikro-orm/apply-indexes"
|
||||
import { parseEntityName } from "./parse-entity-name"
|
||||
import { HasOneWithForeignKey } from "../../relations/has-one-fk"
|
||||
|
||||
type Context = {
|
||||
@@ -141,14 +142,19 @@ export function defineHasOneRelationship(
|
||||
) {
|
||||
const shouldRemoveRelated = !!cascades.delete?.includes(relationship.name)
|
||||
|
||||
let mappedBy: string | undefined = camelToSnakeCase(MikroORMEntity.name)
|
||||
if ("mappedBy" in relationship) {
|
||||
mappedBy = relationship.mappedBy
|
||||
}
|
||||
|
||||
OneToOne({
|
||||
entity: relatedModelName,
|
||||
nullable: relationship.nullable,
|
||||
mappedBy: relationship.mappedBy || camelToSnakeCase(MikroORMEntity.name),
|
||||
...(mappedBy ? { mappedBy } : {}),
|
||||
cascade: shouldRemoveRelated
|
||||
? (["persist", "soft-remove"] as any)
|
||||
: undefined,
|
||||
})(MikroORMEntity.prototype, relationship.name)
|
||||
} as OneToOneOptions<any, any>)(MikroORMEntity.prototype, relationship.name)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -163,12 +169,10 @@ export function defineHasOneWithFKRelationship(
|
||||
) {
|
||||
const foreignKeyName = camelToSnakeCase(`${relationship.name}Id`)
|
||||
const shouldRemoveRelated = !!cascades.delete?.includes(relationship.name)
|
||||
let mappedBy: string | undefined
|
||||
|
||||
let mappedBy: string | undefined = camelToSnakeCase(MikroORMEntity.name)
|
||||
if ("mappedBy" in relationship) {
|
||||
mappedBy = relationship.mappedBy
|
||||
} else {
|
||||
mappedBy = camelToSnakeCase(MikroORMEntity.name)
|
||||
}
|
||||
|
||||
OneToOne({
|
||||
@@ -178,7 +182,7 @@ export function defineHasOneWithFKRelationship(
|
||||
cascade: shouldRemoveRelated
|
||||
? (["persist", "soft-remove"] as any)
|
||||
: undefined,
|
||||
} as any)(MikroORMEntity.prototype, relationship.name)
|
||||
} as OneToOneOptions<any, any>)(MikroORMEntity.prototype, relationship.name)
|
||||
|
||||
Property({
|
||||
type: "string",
|
||||
|
||||
@@ -69,7 +69,9 @@ export abstract class BaseRelationship<T> implements RelationshipType<T> {
|
||||
return {
|
||||
name: relationshipName,
|
||||
nullable: false,
|
||||
mappedBy: this.options.mappedBy,
|
||||
...("mappedBy" in this.options
|
||||
? { mappedBy: this.options.mappedBy }
|
||||
: {}),
|
||||
options: this.options,
|
||||
searchable: this.#searchable,
|
||||
entity: this.#referencedEntity,
|
||||
|
||||
Reference in New Issue
Block a user