0x00 Chapter 5: Fluent & Persisting Models
1.Fluent
is Vapor’s ORM
or object relational mapping
tool.
It’s an abstraction layer between the Vapor application
and the database
2.Models
are the Swift representation of your data and are used throughout Fluent
3.创建一个使用 Fluent
的新项目
新建一个文件夹,比如 vapor_learn
打开终端 cd
到此文件夹:
打开终端,输入 cd
,按个空格
,把文件夹 vapor_learn
拖入终端,最后按 Enter
创建一个名为:TILApp
的项目,(Today I Learn)
vapor new TILApp
是否使用 Fluent
?
输入:y
数据库使用 Postgres
不使用 Leaf
等待创建完成
以下是日志:
Cloning template...
name: TILApp
Would you like to use Fluent?
y/n> y
fluent: Yes
db: Postgres (Recommended)
Would you like to use Leaf?
y/n> n
leaf: No
Generating project files
+ Package.swift
+ main.swift
+ configure.swift
+ routes.swift
+ Todo.swift
+ CreateTodo.swift
+ .gitkeep
+ TodoController.swift
+ AppTests.swift
+ index.leaf
+ .gitkeep
+ Dockerfile
+ docker-compose.yml
+ .gitignore
+ .dockerignore
Creating git repository
Adding first commit
**
**~~**
**~~~~~~**
**~~~~~~~~~~**
**~~~~~~~~~~~~~~**
**~~~~~~~~~~~~~~~~~~**
**~~~~~~~~~~~~~~~~~~~~~~**
**~~~~~~~~~~~~~~~~~~~~~~~~**
**~~~~~~~~~~~~~~~~~~~~~~~~~~**
**~~~~~~~~~~~~~~~~~~~~~~~~~~~~**
**~~~~~~~~~~~~~~~~~~~~~~~~~~~~**
**~~~~~~~~~~~~~~~~~~~~~++++~~~**
**~~~~~~~~~~~~~~~~~~~++++~~~**
***~~~~~~~~~~~~~~~++++~~~***
****~~~~~~~~~~++++~~****
*****~~~~~~~~~*****
*************
_ __ ___ ___ ___
\ \ / / /\ | |_) / / \ | |_)
\_\/ /_/--\ |_| \_\_/ |_| \
a web framework for Swift
Project TILApp has been created!
Use cd 'TILApp' to enter the project directory
Use vapor xcode to open the project in Xcode
看图片更清晰:
使用 cd TILApp
进入项目
使用 vapor xcode
可以在 Xocde
中打开项目
项目结构如下:
TILApp
--- Package.swift
--- Public
--- Resources
--- Views
--- index.leaf
--- Sources
--- App
--- Controllers
--- TodoController.swift
--- Migrations
--- CreateTodo.swift
--- Models
--- Todo.swift
--- configure.swift
--- routes.swift
--- Run
--- main.swift
--- Tests
--- AppTests
--- AppTests.swift
--- docker-compose.yml
--- Dockerfile
看图片更清晰:
4.删除模板中自动生成的一些文件:
依次输入以下 3
个命令:
rm -rf Sources/App/Models/*
rm -rf Sources/App/Migrations/*
rm -rf Sources/App/Controllers/*
删除 configure.swift
中的代码:
app.migrations.add(CreateTodo())
删除 routes.swift
中的代码:
try app.register(collection: TodoController())
5.在 Models
文件夹内,新建模型文件:
Acronym.swift
,代表要提交的数据
// 1
final class Acronym: Model {
// 2
static let schema = "acronyms"
// 3
@ID
var id: UUID?
// 4
@Field(key: "short")
var short: String
@Field(key: "long")
var long: String
// 5
init() {}
// 6
init(id: UUID? = nil, short: String, long: String) {
self.id = id
self.short = short
self.long = long
}
}
// 1. Define a class that conforms to Model
.
// 2. Specify the schema
as required by Model
. This is the name of the table
in the database
.
// 3. Define an optional
id property that stores the ID
of the model
, if one has been set. This is annotated with Fluent’s @ID property wrapper
. This tells Fluent what to use to look up the model in the database
// 4. Define two String
properties to hold the acronym
and its definition
. These use the @Field property wrapper
to denote a generic database field. The key parameter is the name of the column in the database
数据库表中的字段,列名称
@ID
marks a property as the ID
for that table
@Field
marks the property of a model
as a generic column in the database
// 5. Provide an empty
initializer as required by Model
. Fluent uses this to initialize models returned from database queries.
// 6. Provide an initializer to create the model as required
.
6.在 Migrations
文件夹内,新建文件:
CreateAcronym.swift
,用于创建对应的数据库表
// 1
struct CreateAcronym: Migration {
// 2
func prepare(on database: Database) -> EventLoopFuture<Void> {
database.schema("acronyms") // 3
.id() // 4
.field("short", .string, .required) // 5
.field("long", .string, .required) // 5
.create() // 6
}
// 7
func revert(on database: Database) -> EventLoopFuture<Void> {
database.schema("acronyms").delete()
}
}
// 1. Define a new type, CreateAcronym
that conforms to Migration
.
// 2. Implement prepare(on:)
as required by Migration
. You call this method when you run your migrations.
// 3. Define the table name
for this model. This must match
schema from the model
.
// 4. Define the ID
column in the database.
// 5. Define columns for short
and long
. Set the column type
to string
and mark the columns as required
. This matches the non-optional
String properties in the model. The field names
must match the key of the property wrapper
, not the name of the property itself.
// 6. Create
the table in the database.
// 7. Implement revert(on:)
as required by Migration
. You call this function when you revert your migrations. This deletes
the table referenced with schema(_:)
.
继承自 Migration
实现方法 prepare(on:)
为模型创建表,表名
必须和 模型
的 schema
一样
定义 id
, short
, long
这几个列,指定类型,required
对应 non-optional
.field
的列名,跟 @Field
的 key
对应
7.配置 configure.swift
在 app.databases.use(_:as:)
方法后添加代码:
app.migrations.add(CreateAcronym())
app.logger.logLevel = .debug
try app.autoMigrate().wait()
8.存储数据模型,需要数据库
测试 PostgreSQL
:在 Docker container
里运行 Postgres server
依次 下载
、安装
、打开
软件: docker
地址:https://www.docker.com/get-docker
使用终端创建数据库:
docker run --name postgres \
-e POSTGRES_DB=vapor_database \
-e POSTGRES_USER=vapor_username \
-e POSTGRES_PASSWORD=vapor_password \
-p 5432:5432 -d postgres
运行一个名为 postgres
的容器
指定 数据库名
,用户名
,密码
指定 端口
,默认是 5432
检查数据库是否运行:
docker ps
有以下日志,表示成功:
CONTAINER ID | IMAGE | COMMAND | CREATED | STATUS | PORTS | NAMES |
---|---|---|---|---|---|---|
53dc8048acaf | postgres | “docker-entrypoint.s…” | 8 months ago | Up 3 seconds | 0.0.0.0:5432->5432/tcp, :::5432->5432/tcp | postgres |
9.最后,提交模型数据到数据库
9.1.让模型 Acronym
遵守 Content
协议
在 Acronym.swift
最后添加:
extension Acronym: Content {}
Since Acronym
already conforms to Codable
via Model
, you don’t have to add anything else.
9.2.定义 路由
:
接收数据,解析 JSON
,转成 Acronym
模型,存入
数据库
在 routes.swift
文件中,在 routes
方法内添加:
// 1
app.post("api", "acronyms") { req -> EventLoopFuture<Acronym> in
// 2
let acronym = try req.content.decode(Acronym.self)
// 3
return acronym.save(on: req.db).map {
// 4
acronym
}
}
// 1. Register a new route at /api/acronyms
that accepts a POST
request and returns EventLoopFuture<Acronym>
. It returns the acronym
once it’s saved.
// 2. Decode the request’s JSON
into an Acronym
model using Codable
.
// 3. Save the model using Fluent
and the database
from Request.
// 4. save(on:)
returns EventLoopFuture<Void>
so use map
to return the acronym
when the save completes.
10.使用 Rested
提交数据
把代码 运行
起来后,就可以提交数据了
url: http://127.0.0.1:8080/api/acronyms
method: POST
parameters: {"long": "Tomorrow is a good day", "short": "TGD"}
参数提交格式,选择 JSON 形式: JSON-encoded
点击右下角的 Send Request
发起请求
成功后,会返回对应的模型数据,id
字段被赋值了
如下图所示:
11.运行代码
在终端进入到项目的根目录:TILApp
运行命令:vapor run
或者 swift run
会自动下载所有的依赖文件
依赖文件会有很多很多
这一步耗时会 很长很长很长......
你们可能在这 最后一步
就 放弃了
😂
反正我打算新建一个演示项目时
整了一天
,都没有搞好 😂😂
Updating https://github.com/vapor/fluent.git
Updating https://github.com/vapor/fluent-postgres-driver.git
Updating https://github.com/vapor/vapor.git
Updating https://github.com/vapor/fluent-kit.git
Fetching https://github.com/vapor/async-kit.git
Updating https://github.com/vapor/postgres-kit.git
Fetching https://github.com/vapor/postgres-nio.git
Fetching https://github.com/vapor/sql-kit.git
Fetching https://github.com/apple/swift-nio-transport-services.git
error: Failed to clone https://github.com/apple/swift-nio-transport-services.git:
Cloning into bare repository '/Users/apple/vapor_learn/1/TILApp/.build/repositories/swift-nio-transport-services-91c80623'...
fatal: unable to access 'https://github.com/apple/swift-nio-transport-services.git/': LibreSSL SSL_connect: SSL_ERROR_SYSCALL in connection to github.com:443
VaporToolbox/exec.swift:55: Fatal error: result 1
Illegal instruction: 4
拉到某个库的时候,就可能失败了 😂
fatal: unable to access 'https://github.com/apple/swift-nio-transport-services.git/': LibreSSL SSL_connect: SSL_ERROR_SYSCALL in connection to github.com:443
找了好多方案,就是解决不了
这实在是,无话可说了~
0x01 最后
12.如果你们成功了!
终端会输出以下:
[ NOTICE ] Server starting on http://127.0.0.1:8080 (Vapor/HTTP/Server/HTTPServer.swift:270)
表示服务启动了
祝你们好运 😂
0x02 后续
直到今天(2022-03-29 18:48:19)
更新 系统
后:Mac 12.3
更新 Xcode
后:Xcode 13.3
新建项目:vapor new TILApp
终于能够再次跑起来了!😂😂😂
忍不住
地想要记录下来
终端 cd
到 TILApp
目录后
运行:swift run
以下是成功日志:
虽然有警告,但程序员通常对应它视而不见😂
Updating https://github.com/vapor/fluent-postgres-driver.git
Updating https://github.com/vapor/vapor.git
Updating https://github.com/vapor/fluent.git
Updated https://github.com/vapor/fluent-postgres-driver.git (2.65s)
Updated https://github.com/vapor/fluent.git (2.65s)
Updated https://github.com/vapor/vapor.git (2.65s)
Computing version for https://github.com/vapor/fluent.git
Computed https://github.com/vapor/fluent.git at 4.4.0 (0.02s)
Updating https://github.com/vapor/fluent-kit.git
Updating https://github.com/vapor/fluent-kit.git
Updated https://github.com/vapor/fluent-kit.git (1.29s)
Computing version for https://github.com/vapor/fluent-postgres-driver.git
Computed https://github.com/vapor/fluent-postgres-driver.git at 2.2.6 (0.02s)
Updating https://github.com/vapor/fluent-kit.git
Updating https://github.com/vapor/async-kit.git
Updating https://github.com/vapor/postgres-kit.git
Updated https://github.com/vapor/fluent-kit.git (120.51s)
Updating https://github.com/vapor/async-kit.git
Updated https://github.com/vapor/async-kit.git (20.05s)
Updating https://github.com/vapor/postgres-kit.git
Updated https://github.com/vapor/postgres-kit.git (1.30s)
Computing version for https://github.com/vapor/postgres-kit.git
Computed https://github.com/vapor/postgres-kit.git at 2.6.0 (0.02s)
Updating https://github.com/vapor/async-kit.git
Updating https://github.com/vapor/sql-kit.git
Updating https://github.com/vapor/postgres-nio.git
Updated https://github.com/vapor/postgres-nio.git (1.32s)
Updated https://github.com/vapor/async-kit.git (5.65s)
Updated https://github.com/vapor/sql-kit.git (5.65s)
Computing version for https://github.com/vapor/sql-kit.git
Computed https://github.com/vapor/sql-kit.git at 3.16.0 (0.03s)
Updating https://github.com/apple/swift-log.git
Updating https://github.com/apple/swift-nio.git
Updated https://github.com/apple/swift-log.git (49.37s)
Updated https://github.com/apple/swift-nio.git (49.37s)
Computing version for https://github.com/vapor/postgres-nio.git
Computed https://github.com/vapor/postgres-nio.git at 1.9.0 (0.02s)
Updating https://github.com/apple/swift-crypto.git
Updating https://github.com/apple/swift-nio-ssl.git
Updating https://github.com/apple/swift-metrics.git
Updated https://github.com/apple/swift-crypto.git (1.31s)
Updating https://github.com/apple/swift-nio-transport-services.git
Updated https://github.com/apple/swift-nio-transport-services.git (1.16s)
Updated https://github.com/apple/swift-nio-ssl.git (44.55s)
Updated https://github.com/apple/swift-metrics.git (44.55s)
Computing version for https://github.com/apple/swift-nio-transport-services.git
Computed https://github.com/apple/swift-nio-transport-services.git at 1.11.4 (0.02s)
Computing version for https://github.com/apple/swift-log.git
Computed https://github.com/apple/swift-log.git at 1.4.2 (0.02s)
Computing version for https://github.com/apple/swift-nio.git
Computed https://github.com/apple/swift-nio.git at 2.39.0 (0.02s)
Computing version for https://github.com/apple/swift-metrics.git
Computed https://github.com/apple/swift-metrics.git at 2.3.0 (0.02s)
Computing version for https://github.com/apple/swift-nio-ssl.git
Computed https://github.com/apple/swift-nio-ssl.git at 2.18.0 (0.02s)
Computing version for https://github.com/vapor/fluent-kit.git
Computed https://github.com/vapor/fluent-kit.git at 1.23.2 (0.02s)
Computing version for https://github.com/vapor/async-kit.git
Computed https://github.com/vapor/async-kit.git at 1.11.1 (0.02s)
Computing version for https://github.com/vapor/vapor.git
Computed https://github.com/vapor/vapor.git at 4.55.3 (0.03s)
Updating https://github.com/vapor/multipart-kit.git
Updating https://github.com/apple/swift-nio-extras.git
Updating https://github.com/vapor/websocket-kit.git
Updated https://github.com/apple/swift-nio-extras.git (120.49s)
Updated https://github.com/vapor/websocket-kit.git (120.49s)
Updating https://github.com/apple/swift-nio-http2.git
Updating https://github.com/vapor/console-kit.git
Fetching https://github.com/swift-server/swift-backtrace.git
Updated https://github.com/apple/swift-nio-http2.git (68.32s)
Updated https://github.com/vapor/console-kit.git (68.32s)
Fetching https://github.com/swift-server/async-http-client.git
Fetching https://github.com/vapor/routing-kit.git
[883/20948] Fetching objectsFetched https://github.com/swift-server/swift-backtrace.git (70.93s)
Updating https://github.com/vapor/multipart-kit.git
[3514/30681] Fetching objectsUpdated https://github.com/vapor/multipart-kit.git (1.24s)
[22895/30681] Fetching objectsSkipping cache due to an error: Failed to clone repository https://github.com/swift-server/async-http-client.git:
Cloning into bare repository '/Users/apple/Library/Caches/org.swift.swiftpm/repositories/async-http-client-0b7b4e02'...
remote: Enumerating objects: 9733, done.
remote: Counting objects: 100% (3385/3385), done.
remote: Compressing objects: 100% (1082/1082), done.
error: 3672 bytes of body are still expectedKiB | 4.00 KiB/s
fetch-pack: unexpected disconnect while reading sideband packets
fatal: early EOF
fatal: fetch-pack: invalid index-pack output
Fetched https://github.com/vapor/routing-kit.git (236.43s)
[30681/30681] Fetching objects
Fetched https://github.com/swift-server/async-http-client.git (244.35s)
Computing version for https://github.com/apple/swift-nio-http2.git
Computed https://github.com/apple/swift-nio-http2.git at 1.20.1 (1.94s)
Updating https://github.com/vapor/multipart-kit.git
Computing version for https://github.com/swift-server/async-http-client.git
Updated https://github.com/vapor/multipart-kit.git (1.27s)
Computed https://github.com/swift-server/async-http-client.git at 1.9.0 (2.02s)
Computing version for https://github.com/apple/swift-nio-extras.git
Computed https://github.com/apple/swift-nio-extras.git at 1.10.2 (0.74s)
Computing version for https://github.com/vapor/multipart-kit.git
Computed https://github.com/vapor/multipart-kit.git at 4.5.1 (0.71s)
Computing version for https://github.com/swift-server/swift-backtrace.git
Computed https://github.com/swift-server/swift-backtrace.git at 1.3.1 (0.72s)
Computing version for https://github.com/vapor/routing-kit.git
Computed https://github.com/vapor/routing-kit.git at 4.3.1 (0.69s)
Computing version for https://github.com/vapor/websocket-kit.git
Computed https://github.com/vapor/websocket-kit.git at 2.3.1 (0.74s)
Computing version for https://github.com/vapor/console-kit.git
Computed https://github.com/vapor/console-kit.git at 4.2.7 (0.75s)
Computing version for https://github.com/apple/swift-crypto.git
Computed https://github.com/apple/swift-crypto.git at 2.0.5 (0.74s)
Creating working copy for https://github.com/apple/swift-nio-http2.git
Working copy of https://github.com/apple/swift-nio-http2.git resolved at 1.20.1
Creating working copy for https://github.com/apple/swift-nio.git
Working copy of https://github.com/apple/swift-nio.git resolved at 2.39.0
Creating working copy for https://github.com/vapor/vapor.git
Working copy of https://github.com/vapor/vapor.git resolved at 4.55.3
Creating working copy for https://github.com/vapor/routing-kit.git
Working copy of https://github.com/vapor/routing-kit.git resolved at 4.3.1
Creating working copy for https://github.com/apple/swift-log.git
Working copy of https://github.com/apple/swift-log.git resolved at 1.4.2
Creating working copy for https://github.com/apple/swift-nio-ssl.git
Working copy of https://github.com/apple/swift-nio-ssl.git resolved at 2.18.0
Creating working copy for https://github.com/vapor/postgres-kit.git
Working copy of https://github.com/vapor/postgres-kit.git resolved at 2.6.0
Creating working copy for https://github.com/vapor/fluent-kit.git
Working copy of https://github.com/vapor/fluent-kit.git resolved at 1.23.2
Creating working copy for https://github.com/vapor/async-kit.git
Working copy of https://github.com/vapor/async-kit.git resolved at 1.11.1
Creating working copy for https://github.com/vapor/postgres-nio.git
Working copy of https://github.com/vapor/postgres-nio.git resolved at 1.9.0
Creating working copy for https://github.com/swift-server/async-http-client.git
Working copy of https://github.com/swift-server/async-http-client.git resolved at 1.9.0
Creating working copy for https://github.com/apple/swift-nio-extras.git
Working copy of https://github.com/apple/swift-nio-extras.git resolved at 1.10.2
Creating working copy for https://github.com/apple/swift-nio-transport-services.git
Working copy of https://github.com/apple/swift-nio-transport-services.git resolved at 1.11.4
Creating working copy for https://github.com/vapor/fluent.git
Working copy of https://github.com/vapor/fluent.git resolved at 4.4.0
Creating working copy for https://github.com/vapor/sql-kit.git
Working copy of https://github.com/vapor/sql-kit.git resolved at 3.16.0
Creating working copy for https://github.com/apple/swift-metrics.git
Working copy of https://github.com/apple/swift-metrics.git resolved at 2.3.0
Creating working copy for https://github.com/swift-server/swift-backtrace.git
Working copy of https://github.com/swift-server/swift-backtrace.git resolved at 1.3.1
Creating working copy for https://github.com/apple/swift-crypto.git
Working copy of https://github.com/apple/swift-crypto.git resolved at 2.0.5
Creating working copy for https://github.com/vapor/console-kit.git
Working copy of https://github.com/vapor/console-kit.git resolved at 4.2.7
Creating working copy for https://github.com/vapor/multipart-kit.git
Working copy of https://github.com/vapor/multipart-kit.git resolved at 4.5.1
Creating working copy for https://github.com/vapor/fluent-postgres-driver.git
Working copy of https://github.com/vapor/fluent-postgres-driver.git resolved at 2.2.6
Creating working copy for https://github.com/vapor/websocket-kit.git
Working copy of https://github.com/vapor/websocket-kit.git resolved at 2.3.1
Building for debugging...
/Users/apple/vapor_learn/2/TILApp/.build/checkouts/postgres-kit/Sources/PostgresKit/PostgresConnectionSource.swift:53:39: warning: 'connect(to:tlsConfiguration:serverHostname:logger:on:)' is deprecated: Use the new connect method that allows you to connect and authenticate in a single step
return PostgresConnection.connect(
^
/Users/apple/vapor_learn/2/TILApp/.build/checkouts/postgres-kit/Sources/PostgresKit/PostgresConnectionSource.swift:53:39: note: use 'connect(on:configuration:id:logger:)' instead
return PostgresConnection.connect(
^~~~~~~
connect
/Users/apple/vapor_learn/2/TILApp/.build/checkouts/postgres-kit/Sources/PostgresKit/PostgresConnectionSource.swift:60:29: warning: 'authenticate(username:database:password:logger:)' is deprecated: Use the new connect method that allows you to connect and authenticate in a single step
return conn.authenticate(
^
/Users/apple/vapor_learn/2/TILApp/.build/checkouts/postgres-kit/Sources/PostgresKit/PostgresConnectionSource.swift:60:29: note: use 'connect(on:configuration:id:logger:)' instead
return conn.authenticate(
^~~~~~~~~~~~
connect
/Users/apple/vapor_learn/2/TILApp/.build/checkouts/postgres-kit/Sources/PostgresKit/PostgresConnectionSource.swift:53:39: warning: 'connect(to:tlsConfiguration:serverHostname:logger:on:)' is deprecated: Use the new connect method that allows you to connect and authenticate in a single step
return PostgresConnection.connect(
^
/Users/apple/vapor_learn/2/TILApp/.build/checkouts/postgres-kit/Sources/PostgresKit/PostgresConnectionSource.swift:53:39: note: use 'connect(on:configuration:id:logger:)' instead
return PostgresConnection.connect(
^~~~~~~
connect
/Users/apple/vapor_learn/2/TILApp/.build/checkouts/postgres-kit/Sources/PostgresKit/PostgresConnectionSource.swift:60:29: warning: 'authenticate(username:database:password:logger:)' is deprecated: Use the new connect method that allows you to connect and authenticate in a single step
return conn.authenticate(
^
/Users/apple/vapor_learn/2/TILApp/.build/checkouts/postgres-kit/Sources/PostgresKit/PostgresConnectionSource.swift:60:29: note: use 'connect(on:configuration:id:logger:)' instead
return conn.authenticate(
^~~~~~~~~~~~
connect
/Users/apple/vapor_learn/2/TILApp/.build/checkouts/postgres-kit/Sources/PostgresKit/PostgresConnectionSource.swift:53:39: warning: 'connect(to:tlsConfiguration:serverHostname:logger:on:)' is deprecated: Use the new connect method that allows you to connect and authenticate in a single step
return PostgresConnection.connect(
^
/Users/apple/vapor_learn/2/TILApp/.build/checkouts/postgres-kit/Sources/PostgresKit/PostgresConnectionSource.swift:53:39: note: use 'connect(on:configuration:id:logger:)' instead
return PostgresConnection.connect(
^~~~~~~
connect
/Users/apple/vapor_learn/2/TILApp/.build/checkouts/postgres-kit/Sources/PostgresKit/PostgresConnectionSource.swift:60:29: warning: 'authenticate(username:database:password:logger:)' is deprecated: Use the new connect method that allows you to connect and authenticate in a single step
return conn.authenticate(
^
/Users/apple/vapor_learn/2/TILApp/.build/checkouts/postgres-kit/Sources/PostgresKit/PostgresConnectionSource.swift:60:29: note: use 'connect(on:configuration:id:logger:)' instead
return conn.authenticate(
^~~~~~~~~~~~
connect
[1956/1956] Linking Run
Build complete! (109.21s)
[ NOTICE ] Server starting on http://127.0.0.1:8080
0x03 我的作品
欢迎体验我的作品之一:小五笔 86 版
学习五笔的好帮手!
App Store
搜索即可~