Skip to content

Commit

Permalink
chore(utils): DML#hasOne allow mappedBy to not be defined (#10442)
Browse files Browse the repository at this point in the history
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`
  • Loading branch information
adrien2p authored Dec 5, 2024
1 parent 223bcff commit 90d7f4f
Show file tree
Hide file tree
Showing 4 changed files with 113 additions and 11 deletions.
5 changes: 5 additions & 0 deletions .changeset/good-moles-camp.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@medusajs/utils": patch
---

chore(utils): DML#hasOne allow mappedBy to not be defined
91 changes: 91 additions & 0 deletions packages/core/utils/src/dml/__tests__/entity-builder.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
Expand Down Expand Up @@ -2899,6 +2986,7 @@ describe("Entity builder", () => {
username: model.text(),
email: model.hasOne(() => email, {
foreignKey: true,
mappedBy: undefined,
}),
})

Expand Down Expand Up @@ -3010,6 +3098,7 @@ describe("Entity builder", () => {
emails: model
.hasOne(() => email, {
foreignKey: true,
mappedBy: undefined,
})
.nullable(),
})
Expand Down Expand Up @@ -3265,6 +3354,7 @@ describe("Entity builder", () => {
entity: "Email",
nullable: false,
cascade: ["persist", "soft-remove"],
mappedBy: "user",
},
email_id: {
columnType: "text",
Expand Down Expand Up @@ -3440,6 +3530,7 @@ describe("Entity builder", () => {
entity: "Email",
nullable: false,
cascade: ["persist", "soft-remove"],
mappedBy: "user",
},
email_id: {
columnType: "text",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,18 @@ import {
ManyToOne,
OneToMany,
OneToOne,
OneToOneOptions,
OnInit,
Property,
rel,
} from "@mikro-orm/core"
import { camelToSnakeCase, pluralize } from "../../../common"
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 { 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 = {
Expand Down Expand Up @@ -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)
}

/**
Expand All @@ -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({
Expand All @@ -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",
Expand Down
4 changes: 3 additions & 1 deletion packages/core/utils/src/dml/relations/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down

0 comments on commit 90d7f4f

Please sign in to comment.