0x00 Chapter 9: Parent-Child Relationships
5.Setting up the relationship
Because a
user
owns each
acronym
, you add a
user
property to the
acronym
.
a. open Acronym.swift
add a new property after
var long: String
@Parent(key: "userID")
var user: User
Replace the initializer
init(id: UUID? = nil, short: String, long: String, userID: User.IDValue) {
self.id = id
self.short = short
self.long = long
self.$user.id = userID
}
b. open CreateAcronym.swift
Before
.create()
add the following line
.field("userID", .uuid, .required)
c. open AcronymsController.swift
add the following code At the bottom of file
struct CreateAcronymData: Content {
let short: String
let long: String
let userID: UUID
}
update the body of
createHandler(_:)
:
func createHandler(_ req: Request) throws -> EventLoopFuture<Acronym> {
//let acronym = try req.content.decode(Acronym.self)
let data = try req.content.decode(CreateAcronymData.self)
let acronym = Acronym(short: data.short, long: data.long, userID: data.userID)
return acronym.save(on: req.db).map { acronym }
}
update the body of
updateHandler(_:)
:
func updateHandler(_ req: Request) throws -> EventLoopFuture<Acronym> {
//let updatedAcronym = try req.content.decode(Acronym.self)
let updateData = try req.content.decode(CreateAcronymData.self)
return Acronym.find(req.parameters.get("acronymID"), on: req.db)
.unwrap(or: Abort(.notFound)).flatMap { acronym in
acronym.short = updateData.short
acronym.long = updateData.long
acronym.$user.id = updateData.userID
return acronym.save(on: req.db).map {
acronym
}
}
}
6.reset the database
To add the
new column
to the table, you must
delete
the database so
Fluent
will run the migration again.
如果資料庫在運作,先停止
打開終端:
docker stop postgres
删除資料庫:
docker rm postgres
重新建立資料庫:
docker run --name postgres \
-e POSTGRES_DB=vapor_database \
-e POSTGRES_USER=vapor_username \
-e POSTGRES_PASSWORD=vapor_password \
-p 5432:5432 \
-d postgres
因為資料庫被删除了
是以資料需要重新生成
參考前面幾遍文章
送出
acronym
資料:
url:
http://127.0.0.1:8080/api/acronyms
method:
POST
parameters:
{"short": "MMD", "long":"麼麼哒","userID":"A1B2C3D4"}
userID
是随便寫的
後面加了外鍵限制後,就不能随便寫了
送出
user
資料:
url:
http://127.0.0.1:8080/api/users
method:
POST
parameters:
{"name": "老三", "username":"張三"}
7.Querying the relationship
Getting the parent
open AcronymsController.swift
add a
new
route handler after
sortedHandler(_:)
:
func getUserHandler(_ req: Request) throws -> EventLoopFuture<User> {
Acronym.find(req.parameters.get("acronymID"), on: req.db)
.unwrap(or: Abort(.notFound))
.flatMap { acronym in
acronym.$user.get(on: req.db)
}
}
Register the route handler at the
end
of
boot(routes:)
acronymsRoutes.get(":acronymID", "user", use: getUserHandler)
根據
acronym
的
ID
查詢
user
url:
http://127.0.0.1:8080/api/acronyms/F3320C03-8570-443F-A8AB-071681470DA4/user
method:
GET
parameters:
無
通過浏覽器直接通路
傳回資料,格式化後:
{
"id": "0DFD7B3A-F38D-43AA-8DD5-26B71E4FE3D0",
"username": "張三",
"name": "老三"
}
Getting the children
open User.swift
add a
new
property below
var username: String
@Children(for: \.$user)
var acronyms: [Acronym]
open UsersController.swift
add a
new
route handler after
getHandler(_:)
:
func getAcronymsHandler(_ req: Request) throws -> EventLoopFuture<[Acronym]> {
User.find(req.parameters.get("userID"), on: req.db)
.unwrap(or: Abort(.notFound))
.flatMap { user in
user.$acronyms.get(on: req.db)
}
}
Register the route handler at the
end
of
boot(routes:)
:
usersRoute.get(":userID", "acronyms", use: getAcronymsHandler)
根據
user
的
ID
查詢
acronym
url:
http://127.0.0.1:8080/api/users/0DFD7B3A-F38D-43AA-8DD5-26B71E4FE3D0/acronyms
method:
GET
parameters:
無
通過浏覽器直接通路
傳回資料:
[
{
"id": "F3320C03-8570-443F-A8AB-071681470DA4",
"short": "MMD",
"long": "麼麼哒",
"user": {
"id": "0DFD7B3A-F38D-43AA-8DD5-26B71E4FE3D0"
}
}
]
8.Foreign key constraints
外鍵限制
describe a
link
between
two
tables
Using
foreign key constraints
has a number of
benefits
:
- It ensures you
create acronyms with users that can’t
existdon’t
- You
delete users until you’ve can’t
all their acronyms.deleted
- You
delete the user table until you’ve can’t
the acronym table.deleted
9.Foreign key constraints are set up in the migration.
Open
CreateAcronym.swift
,
and replace
.field("userID", .uuid, .required)
with the following
.field("userID", .uuid, .required, .references("users", "id"))
Finally, because you’re linking the acronym’s
userID
property to the
User
table
you must
create
the
User
table
first
In
configure.swift
, move the
User
migration to before the
Acronym
migration:
app.migrations.add(CreateUser())
app.migrations.add(CreateAcronym())
最後再重新操作一遍:
停止資料庫:
docker stop postgres
删除資料庫:
docker rm postgres
重新建立資料庫:
docker run --name postgres \
-e POSTGRES_DB=vapor_database \
-e POSTGRES_USER=vapor_username \
-e POSTGRES_PASSWORD=vapor_password \
-p 5432:5432 \
-d postgres
送出
acronym
時,如果
userID
随便寫
比如把
userID
最後一位改一下
會導緻錯誤:
violates foreign key constraint
違反外鍵限制
{
"error": true,
"reason": "server: insert or update on table \"acronyms\" violates foreign key constraint \"acronyms_userID_fkey\" (ri_ReportViolation)"
}