Fluent的关系可以让你用三种不同的方式来描述你的模型:
类型Type | 关系Relations |
---|---|
一对一One to One | 父/子Parent / Child |
一对多One to Many | 父/孩子们Parent / Children |
多对多Many to Many | 兄弟姐妹Siblings |
一对多(One to Many)
我们将从一对多开始,因为它是最简单的关系类型。
采取以下数据库模式:
users
id | name |
---|---|
<id type> | string |
pets
id | name | user_id |
---|---|---|
<id type> | string | <id type> |
瞧一瞧
有关如何创建模式的更多信息,请
访问数据库准备指南。
这里每只宠物只有一个拥有者(一个用户),每个拥有者可以拥有多个宠物。这是一对多的关系。一位业主有很多宠物。
提示
使用builder.foreignId()
创建外IDS一样user_id
。这将自动创建外键约束并遵循预设的键命名约定。
孩子们(Children)
要访问用户的宠物,我们将使用该Children
关系。
extension User {
var pets: Children<User, Pet> {
return children()
}
}
想象一下孩子的关系为Children<Parent, Child>
或Children<From, To>
。这里我们从用户类型到宠物类型有关。
我们现在可以使用这种关系来获取所有用户的宠物。
let pets = try user.pets.all() // [Pet]
这将创建类似于以下的SQL
SELECT * FROM `pets` WHERE `user_id` = '...';
关系的工作类似于查询(Query) 。
let pet = try user.pets.filter("name", "Spud").first()
父(Parent)
要从宠物身上获取宠物的所有者,我们将使用该Parent
关系。
extension Pet {
let userId: Identifier
...
var owner: Parent<Pet, User> {
return parent(id: userId)
}
}
假设父关系为Parent<Child, Parent>
或Parent<From, To>
。这里我们从宠物类型到父类型。
注意
请注意,Parent
关系需要传入一个标识符。确保在模型的init(row:)
方法中加载此标识符。
我们现在可以使用这种关系来获得宠物的主人。
let owner = try pet.owner.get() // User?
迁移(Migration)
将父标识符添加到子表可以使用.parent()
模式构建器上的方法来完成。
try database.create(Pet.self) { builder in
...
builder.parent(User.self)
}
一对一(One to One)
一对一关系与一对多关系完全一样。您可以使用前一个示例中的代码,并简单地调用.first()
和来自父类型的所有调用。
但是,您可以为此添加方便。我们假设我们想将上一个例子从一对多改为一对一。
extension User {
func pet() throws -> Pet? {
return try children().first()
}
}
多对多(Many to Many)
许多关系需要一个表来存储哪个模型与哪个模型相关。这个表被称为“主表”。
你可以使用任何你想要的实体作为主元,但是Fluent提供了一个默认的主元(Pivot
)。
采取以下模式。
pets
id | name |
---|---|
<id type> | string |
pet_toy
id | pet_id | toy_id |
---|---|---|
<id type> | <id type> | <id type> |
toys
id | name |
---|---|
<id type> | string |
在这里,每个宠物都可以拥有许多玩具,而且每个玩具都可以归属于许多宠物。这是多对多关系。
兄弟姐妹(Siblings)
为了表示这种多对多的关系,我们将使用这种Siblings
关系。
extension Pet {
var toys: Siblings<Pet, Toy, Pivot<Pet, Toy>> {
return siblings()
}
}
想像兄弟姐妹的关系Siblings<From, To, Through>
。在这里,我们通过pet/toy枢纽把宠物类型和玩具类型联系起来。
注意
一般的语法可能看起来有点吓人,但它允许一个非常强大的API。
随着宠物的这种关系,我们可以获取一只宠物的所有玩具。
let toys = pet.toys.all() // [Toy]
兄弟姐妹的关系类似于查询(queries) 和父/子关系
迁移(Migration)
如果您使用的是Pivot
类型,则可以将其添加到您的Droplet的准备阵列中。
drop.preparations.append(Pivot<Pet, Toy>)
如果您使用的Pivot
是“直通(through)”模型,那么它还将具有从关系中添加和删除模型的方法。
添加(Add)
要为关系添加一个新模型,请使用.add()
方法。
try pet.toys.add(toy)
注意
新创建的中枢轴(pivot)将被返回。
删除(Remove)
要删除一个与之相关的模型,请使用.remove()
方法。
try pet.toys.remove(toy)
附加(Is Attached)
要检查模型是否相关,请使用.isAttached()
方法。
if try pet.toys.isAttached(to: toy) {
// it is attached
}
自定义抛出(Custom Through)
您可以使用任何实体类型作为您的兄弟姐妹关系中的“ through”实体。
extension User {
var posts: Siblings<User, Post, Comment> {
return siblings()
}
}
在上面的例子中,我们在评论实体上转动以检索用户已经评论过的所有帖子。
只要“ through”实体有一个user_id
和post_id
,那么兄弟姐妹关系就会起作用。
注意
如果Comment
实体不继承PivotProtocol
,那么add
,remove
和isAttached
方法将不可用。