Skip to content

Commit ab82e4c

Browse files
authored
docs: use mappedBy property for all relationship types (medusajs#12973)
1 parent 5527d95 commit ab82e4c

File tree

8 files changed

+175
-121
lines changed

8 files changed

+175
-121
lines changed

www/apps/book/app/learn/fundamentals/data-models/relationships/page.mdx

Lines changed: 70 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -39,16 +39,19 @@ For example:
3939

4040
export const oneToOneHighlights = [
4141
["5", "hasOne", "A user has one email."],
42-
["10", "belongsTo", "An email belongs to a user."],
43-
["11", `"email"`, "The relationship's name in the `User` data model."]
42+
["6", `"user"`, "The relationship's name in the `Email` data model."],
43+
["12", "belongsTo", "An email belongs to a user."],
44+
["13", `"email"`, "The relationship's name in the `User` data model."]
4445
]
4546

4647
```ts highlights={oneToOneHighlights}
4748
import { model } from "@medusajs/framework/utils"
4849

4950
const User = model.define("user", {
5051
id: model.id().primaryKey(),
51-
email: model.hasOne(() => Email),
52+
email: model.hasOne(() => Email, {
53+
mappedBy: "user",
54+
}),
5255
})
5356

5457
const Email = model.define("email", {
@@ -63,15 +66,68 @@ In the example above, a user has one email, and an email belongs to one user.
6366

6467
The `hasOne` and `belongsTo` methods accept a function as the first parameter. The function returns the associated data model.
6568

66-
The `belongsTo` method also requires passing as a second parameter an object with the property `mappedBy`. Its value is the name of the relationship property in the other data model.
69+
Both methods also accept a second parameter object with the property `mappedBy`. Its value is the name of the relationship property in the other data model.
6770

6871
### Optional Relationship
6972

7073
To make the relationship optional on the `hasOne` or `belongsTo` side, use the `nullable` method on either property as explained in [this chapter](../properties/page.mdx#make-property-optional).
7174

75+
### One-to-One Relationship in the Database
76+
77+
When you generate the migrations of data models that have a one-to-one relationship, the migration adds to the table of the data model that has the `belongsTo` property:
78+
79+
1. A column of the format `{relation_name}_id` to store the ID of the record of the related data model. For example, the `email` table will have a `user_id` column.
80+
2. A foreign key on the `{relation_name}_id` column to the table of the related data model.
81+
82+
![Diagram illustrating the relation between user and email records in the database](https://res.cloudinary.com/dza7lstvk/image/upload/v1726733492/Medusa%20Book/one-to-one_cj5np3.jpg)
83+
7284
### One-sided One-to-One Relationship
7385

74-
If the one-to-one relationship is only defined on one side, pass `undefined` to the `mappedBy` property in the `belongsTo` method.
86+
In some use cases, you may want to define a one-to-one relationship only on one side. This means that the other data model does not have a relationship property pointing to the first one.
87+
88+
You can do this either from the `hasOne` or the `belongsTo` side.
89+
90+
#### hasOne Side
91+
92+
By default, the foreign key column is added to the table of the data model that has the `belongsTo` property. For example, if the `Email` data model belongs to the `User` data model, then the foreign key column is added to the `email` table.
93+
94+
If you want to define a one-to-one relationship only on the `User` data model's side (`hasOne` side), you can do so by passing the following properties to the second parameter of the `hasOne` method:
95+
96+
- `foreignKey`: A boolean indicating whether the foreign key column should be added to the table of the data model.
97+
- `mappedBy`: Set to `undefined`, since the relationship is only defined on one side.
98+
99+
For example:
100+
101+
export const oneToOneForeignKeyHighlights = [
102+
["5", "hasOne", "A user has one email."],
103+
["6", "foreignKey", "Add the foreign key column to the `user` table."],
104+
["7", "mappedBy", "Set to `undefined` since the relationship is only defined on the `User` data model."],
105+
]
106+
107+
```ts highlights={oneToOneForeignKeyHighlights}
108+
import { model } from "@medusajs/framework/utils"
109+
110+
const User = model.define("user", {
111+
id: model.id().primaryKey(),
112+
email: model.hasOne(() => Email, {
113+
foreignKey: true,
114+
mappedBy: undefined,
115+
}),
116+
})
117+
118+
const Email = model.define("email", {
119+
id: model.id().primaryKey(),
120+
})
121+
```
122+
123+
In the example above, you add a one-to-one relationship from the `User` data model to the `Email` data model.
124+
125+
The foreign key column is added to the `user` table, and the `Email` data model does not have a relationship property pointing to the `User` data model.
126+
127+
128+
#### belongsTo Side
129+
130+
To define the one-to-one relationship on the `belongsTo` side, pass `undefined` to the `mappedBy` property in the `belongsTo` method's second parameter.
75131

76132
For example:
77133

@@ -94,14 +150,9 @@ const Email = model.define("email", {
94150
})
95151
```
96152

97-
### One-to-One Relationship in the Database
98-
99-
When you generate the migrations of data models that have a one-to-one relationship, the migration adds to the table of the data model that has the `belongsTo` property:
100-
101-
1. A column of the format `{relation_name}_id` to store the ID of the record of the related data model. For example, the `email` table will have a `user_id` column.
102-
2. A foreign key on the `{relation_name}_id` column to the table of the related data model.
153+
In the example above, you add a one-to-one relationship from the `Email` data model to the `User` data model.
103154

104-
![Diagram illustrating the relation between user and email records in the database](https://res.cloudinary.com/dza7lstvk/image/upload/v1726733492/Medusa%20Book/one-to-one_cj5np3.jpg)
155+
The `User` data model does not have a relationship property pointing to the `Email` data model.
105156

106157
---
107158

@@ -118,16 +169,19 @@ For example:
118169

119170
export const oneToManyHighlights = [
120171
["5", "hasMany", "A store has many products."],
121-
["10", "belongsTo", "A product has one store."],
122-
["11", `"products"`, "The relationship's name in the `Store` data model."]
172+
["6", `"store"`, "The relationship's name in the `Product` data model."],
173+
["12", "belongsTo", "A product has one store."],
174+
["13", `"products"`, "The relationship's name in the `Store` data model."]
123175
]
124176

125177
```ts highlights={oneToManyHighlights}
126178
import { model } from "@medusajs/framework/utils"
127179

128180
const Store = model.define("store", {
129181
id: model.id().primaryKey(),
130-
products: model.hasMany(() => Product),
182+
products: model.hasMany(() => Product, {
183+
mappedBy: "store",
184+
}),
131185
})
132186

133187
const Product = model.define("product", {
@@ -165,7 +219,7 @@ For example:
165219

166220
export const manyToManyHighlights = [
167221
["5", "manyToMany", "An order is associated with many products."],
168-
["12", "manyToMany", "A product is associated with many orders."]
222+
["15", "manyToMany", "A product is associated with many orders."]
169223
]
170224

171225
```ts highlights={manyToManyHighlights}
@@ -278,43 +332,6 @@ The `OrderProduct` model defines, aside from the ID, the following properties:
278332

279333
---
280334

281-
## Set Relationship Name in the Other Model
282-
283-
The relationship property methods accept as a second parameter an object of options. The `mappedBy` property defines the name of the relationship in the other data model.
284-
285-
This is useful if the relationship property’s name is different from that of the associated data model.
286-
287-
As seen in previous examples, the `mappedBy` option is required for the `belongsTo` method.
288-
289-
For example:
290-
291-
export const relationNameHighlights = [
292-
["6", `"owner"`, "The relationship's name in the `Email` data model."],
293-
["13", `"email"`, "The relationship's name in the `User` data model."]
294-
]
295-
296-
```ts highlights={relationNameHighlights}
297-
import { model } from "@medusajs/framework/utils"
298-
299-
const User = model.define("user", {
300-
id: model.id().primaryKey(),
301-
email: model.hasOne(() => Email, {
302-
mappedBy: "owner",
303-
}),
304-
})
305-
306-
const Email = model.define("email", {
307-
id: model.id().primaryKey(),
308-
owner: model.belongsTo(() => User, {
309-
mappedBy: "email",
310-
}),
311-
})
312-
```
313-
314-
In this example, you specify in the `User` data model’s relationship property that the name of the relationship in the `Email` data model is `owner`.
315-
316-
---
317-
318335
## Cascades
319336

320337
When an operation is performed on a data model, such as record deletion, the relationship cascade specifies what related data model records should be affected by it.

www/apps/book/generated/edit-dates.mjs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ export const generatedEditDates = {
3636
"app/learn/fundamentals/data-models/manage-relationships/page.mdx": "2025-04-25T14:16:41.124Z",
3737
"app/learn/fundamentals/modules/remote-query/page.mdx": "2024-07-21T21:20:24+02:00",
3838
"app/learn/fundamentals/modules/options/page.mdx": "2025-03-18T15:12:34.510Z",
39-
"app/learn/fundamentals/data-models/relationships/page.mdx": "2025-03-18T07:52:07.421Z",
39+
"app/learn/fundamentals/data-models/relationships/page.mdx": "2025-07-16T09:51:22.141Z",
4040
"app/learn/fundamentals/workflows/compensation-function/page.mdx": "2025-04-24T13:16:00.941Z",
4141
"app/learn/fundamentals/modules/service-factory/page.mdx": "2025-03-18T15:14:13.486Z",
4242
"app/learn/fundamentals/modules/module-links/page.mdx": "2024-09-30T08:43:53.126Z",

0 commit comments

Comments
 (0)