リレーションシップ
モデルを定義したら、`belongsTo` と `hasMany` ヘルパーを使用して、モデル間のリレーションシップを定義できます。各ヘルパーは、モデルにいくつかの動的メソッドを追加します。
belongsTo
1対1のリレーションシップを定義するには、`belongsTo` ヘルパーをインポートし、別のモデルを指すモデルに新しいプロパティを定義します。
import { createServer, Model, belongsTo } from "miragejs"
createServer({
models: {
blogPost: Model.extend({
author: belongsTo(),
}),
author: Model,
},
})
これは、`Author` モデルへの `belongsTo` リレーションシップを定義します。
`belongsTo` ヘルパーは、モデルにいくつかの新しいプロパティとメソッドを追加します。
この場合、`BlogPost` モデルは `authorId` プロパティと、関連付けられた `author` モデルを操作するためないくつかのメソッドを獲得します。
blogPost.authorId // 1
blogPost.authorId = 2 // updates the relationship
blogPost.author // Author instance
blogPost.author = anotherAuthor
blogPost.newAuthor(attrs) // new unsaved author
blogPost.createAuthor(attrs) // new saved author (updates blogPost.authorId in memory only)
`createAuthor` メソッドは新しい作成者を 作成し、すぐに `db` に保存しますが、ブログ投稿の外部キーは*このインスタンスでのみ*更新され、データベースにすぐに永続化されるわけではありません。そのため、`blogPost.authorId` はメモリ内で更新されますが、`db` から `blogPost` を再度フェッチした場合、リレーションシップは永続化されません。
新しい外部キーを永続化するには、新しい作成者を作成した後に `blogPost.save()` を呼び出す必要があります。
hasMany
多対多のリレーションシップを定義するには、hasMany
ヘルパーを使用します。
import { createServer, Model, hasMany } from "miragejs"
createServer({
models: {
blogPost: Model.extend({
comments: hasMany(),
}),
comment: Model,
},
})
このヘルパーは、blogPost
モデルに commentIds
プロパティと、関連付けられた comments
コレクションを操作するためメソッドを追加します。
blogPost.commentIds // [1, 2, 3]
blogPost.commentIds = [2, 3] // updates the relationship
blogPost.comments // array of related comments
blogPost.comments = [comment1, comment2] // updates the relationship
blogPost.newComment(attrs) // new unsaved comment
blogPost.createComment(attrs) // new saved comment (comment.blogPostId is set)
1対1
1対1のリレーションシップは、2つのモデルで belongsTo
ヘルパーを使用して定義できます。
import { createServer, Model, belongsTo } from "miragejs"
createServer({
models: {
supplier: Model.extend({
account: belongsTo(),
}),
account: Model.extend({
supplier: belongsTo(),
}),
},
})
デフォルトでは、Mirage はこれら2つのリレーションシップを互いに逆としてマークし、変更に合わせて同期を保つことができます。たとえば、supplierA.account
が accountB
であった場合、accountB.supplier
は supplierA
を指します。また、supplierA.account
が null
に設定された場合、リレーションシップの同期を維持するために、accountB.supplier
側も null
に設定されます。
1対多
1対多のリレーションシップは、一方のモデルで belongsTo
ヘルパーを使用し、逆のモデルで hasMany
ヘルパーを使用して定義できます。
import { createServer, Model, belongsTo, hasMany } from "miragejs"
createServer({
models: {
user: Model.extend({
comments: hasMany(),
}),
comment: Model.extend({
user: belongsTo(),
}),
},
})
Mirage はこれらを互いに逆としてマークし、変更に合わせて同期を保ちます。
多対多
多対多のリレーションシップは、2つのモデルで hasMany
ヘルパーを使用して定義できます。
import { createServer, Model, hasMany } from "miragejs"
createServer({
models: {
blogPost: Model.extend({
tags: hasMany(),
}),
tag: Model.extend({
blogPosts: hasMany(),
}),
},
})
Mirage はこれらを互いに逆としてマークし、変更に合わせて同期を保ちます。
アソシエーションオプション
belongsTo
および hasMany
リレーションシップ定義をカスタマイズするために、以下のオプションを使用できます。
modelName
関連付けのモデルが関連付け自体とは異なる名前を持つ場合、関連付けに modelName
を指定できます。
例えば、
import { createServer, Model, belongsTo, hasMany } from "miragejs"
createServer({
models: {
user: Model,
annotation: Model,
blogPost: Model.extend({
author: belongsTo("user"),
comments: hasMany("annotation"),
}),
},
})
上記のように、author
と comment
という名前のすべてのメソッドを追加しますが、実際のリレーションシップには User
と Annotation
モデルを使用します。
inverse
多くの場合、リレーションシップは互いに逆になることがあります。
たとえば、次の2つのモデルがあるとします。
import { createServer, Model, belongsTo, hasMany } from "miragejs"
createServer({
models: {
blogPost: Model.extend({
comments: hasMany(),
}),
comment: Model.extend({
blogPost: belongsTo(),
}),
},
})
この場合、blogPost.comments
は Comment モデルのコレクションを指し、それらの Comment モデルのそれぞれは、元の投稿を指す comment.blogPost
リレーションシップを持ちます。
Mirage は多くの場合、2つの異なるモデルの2つのリレーションシップが互いに逆であると推測できますが、明示的にする必要がある場合があります。これは通常、モデルに同じモデルタイプを指す2つのリレーションシップがある場合に発生します。
たとえば、次のスキーマがあるとします。
import { createServer, Model, belongsTo, hasMany } from "miragejs"
createServer({
models: {
user: Model.extend({
blogPosts: hasMany(),
}),
blogPost: Model.extend({
author: belongsTo("user"),
reviewer: belongsTo("user"),
}),
},
})
この場合、Mirage はどのリレーションシップ(blogPost.author
または blogPost.reviewer
)を user.blogPosts
コレクションと同期させるべきかわかりません。そのため、inverse
オプションを使用して、どちらが逆であるかを指定できます。
import { createServer, Model, belongsTo, hasMany } from "miragejs"
createServer({
models: {
user: Model.extend({
blogPosts: hasMany(),
}),
blogPost: Model.extend({
author: belongsTo("user", { inverse: "blogPosts" }),
reviewer: belongsTo("user", { inverse: null }),
}),
},
})
これで、ブログ投稿が user.blogPosts
に追加されると、その投稿の author
が正しく更新されます。
polymorphic
オプションとして { polymorphic: true }
を渡すことで、関連付けが多態関連付けであるかどうかを指定できます。
たとえば、BlogPost
と Picture
モデルがあり、どちらも Comment
を持つことができるとします。モデル定義は次のようになります。
import { createServer, Model, belongsTo, hasMany } from "miragejs"
createServer({
models: {
blogPost: Model.extend({
comments: hasMany(),
}),
picture: Model.extend({
comments: hasMany(),
}),
comment: Model.extend({
commentable: belongsTo({ polymorphic: true }),
}),
},
})
コメントされるモデルは BlogPost
または Picture
のいずれかである可能性があるため、Comment
モデルに commentable
多態リレーションシップを与えます。そのリレーションシップにどのタイプのモデルが存在できるかについての検証が行われないため、commentable
にはタイプがありません。
多態関連付けは、外部キーと build/create メソッドのメソッドシグネチャがわずかに異なります。
let comment = server.schema.comments.create({ text: "foo" })
comment.buildCommentable("blog-post", { title: "Lorem Ipsum" })
comment.createCommentable("blog-post", { title: "Lorem Ipsum" })
// getter
comment.commentableId // { id: 1, type: 'blog-post' }
// setter
comment.commentableId = { id: 2, type: "picture" }
Has-many リレーションシップも多態的である可能性があります。
import { createServer, Model, belongsTo, hasMany } from "miragejs"
createServer({
models: {
car: Model,
watch: Model
user: Model.extend({
things: hasMany({ polymorphic: true })
}),
},
})
let user = server.schema.users.create({ name: "Sam" });
user.buildThing('car', { attrs });
user.createThing('watch', { attrs });
// getter
user.thingIds; // [ { id: 1, type: 'car' }, { id: 3, type: 'watch' }, ... ]
// setter
user.thingIds = [ { id: 2, type: 'watch' }, ... ];
使用可能なすべての ORM メソッドについては、スキーマ、モデル、およびコレクションの API ドキュメントを参照してください。
これらのガイドではシリアライザーについても説明します。シリアライザーでは、モデルとコレクションのシリアル化された形式を本番 API に一致するようにカスタマイズする方法を学習します。
次に、モデル定義を活用してリレーショナルデータのグラフを簡単に作成できるファクトリを見てみましょう。