在Vapor中,我们可以通过创建Pivot<U, T>
对象来创建多对多关系,其中U
和T
是我们想要链接在一起的模型。因此,如果我想要创建一个系统,其中User
可以包含多个File
,而许多File
可以属于许多User
,我将它们关联起来像这样:
var alice = User(name: "Alice")
try! alice.save()
var sales = File(name: "sales.xclx")
try! sales.save()
var pivot = Pivot<User, File>(alice, sales)
try! pivot.save()
我能为我的生活找出的是如何让Pivot<User, File>
包含其他信息?例如,我想知道这个文件何时与Alice关联,或者她对此有什么权限。
在关系数据库中,Fluent为Pivot<User, File>
类型创建此表。
+---------+---------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+---------+---------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| file_id | int(11) | NO | | NULL | |
| user_id | int(11) | NO | | NULL | |
+---------+---------+------+-----+---------+----------------+
但我希望能够代表这样的事情:
+---------+---------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+---------+---------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| file_id | int(11) | NO | | NULL | |
| user_id | int(11) | NO | | NULL | |
| date | date | NO | | NULL | |
| perms | varchar | NO | | READ | |
+---------+---------+------+-----+---------+----------------+
答案 0 :(得分:6)
Pivot<U, T>
对象可以被认为是&#34;最小的&#34;像siblings
这样的透视关系的必填字段。
如果要向此表添加自定义字段,只要具有所需元素,就可以创建自己的类作为数据透视表:
Foo
和Bar
的表名为bar_foo
(小写,按字母顺序排列)id
,bar_id
,foo_id
换句话说,您的pivot类创建的表必须至少包含Pivot<Foo, Bar>
准备创建的元素。
完成此操作后,您可以通过创建和保存数据透视表类的实例来创建新的数据透视关系。
在使用此数据透视表的模型上调用.siblings()
关系时,仍会创建默认的Pivot<U, T>
以执行提取。但是,由于数据透视表中存在必填字段,因此不会产生任何问题。
答案 1 :(得分:4)
所以在得到Andy描述的相同问题并要求解决Vapor Slack的问题后,我被重定向到这里。
我对Tanner提出的解决方案的实现(使用PostgreSQL)可以找到here
关键是Rating
模型:
Model
子类entity
名称为movie_user
(由Tanner描述,按字母顺序排列相关模型的名称)userId
字段(映射到"user_id"
)和movieId
(映射到"movie_id"
),两者都是Node
类型。prepare(Database)
中,它再次使用名称"movie_user"
,并将Id字段定义为Int
s。通过该设置,您可以定义以下关系便捷方法:
在Movie
:所有评估者
extension Movie {
func raters() throws -> Siblings<User> {
return try siblings()
}
}
在User
上:所有评分的电影
extension User {
func ratedMovies() throws -> Siblings<Movie> {
return try siblings()
}
}
可以像这样添加电影(针对用户)的新评级:
ratings.post() { request in
var rating = try Rating(node: request.json)
try rating.save()
return rating
}
由于Rating
是Model子类,我们可以直接从请求JSON创建它。这要求客户端发送符合Rating
类的节点结构的JSON文档:
{
"user_id": <the rating users id>,
"movie_id": <the id of the movie to be rated>,
"stars": <1-5 or whatever makes sense for your usecase>
}
要获得给定电影的所有实际Rating
,您似乎必须手动解决关系(至少我是这么认为的,也许有人可以给我一个关于如何更好地做到这一点的提示):< / p>
let ratings = try Rating.query().filter("movie_id", movieId).all()
此外,似乎没有办法以某种方式计算数据库的平均值。我会喜欢这样的工作:
// this is now how it works
let averageRating = try Rating.query().filter("movie_id", movieId).average("stars")
所以我希望这可以帮助任何人遇到这个问题。感谢为Vapor项目做出贡献的所有优秀人才!
请注意@WERUreo指出创建评级的部分缺失。