在上一篇Vapor奇幻之旅(04Routing)中我介绍了Routing的写法,作为一个web应用,数据库是必不可少的,而Fluent则是管理数据的一个抽象层,可以支持数据库的增删改查等操作,默认的FluentProvider支持sqlite数据库,也就是说在没有任何数据库配置的情况下,可以通过Fluent Provider中的内存数据库来快速加载SQLite数据库,这样做的好处是可以轻松的进行接口测试。
目前Vapor支持的数据库如下:
数据库类型 | Key | Package | Class | 是否来自官方 |
---|---|---|---|---|
Memory | memory | Fluent Provider | Fluent.MemoryDriver | Yes |
SQlite | sqlite | Fluent Provider | Fluent.SQLiteDriver | Yes |
MySQL | mysql | MySQLProvider | MySQLDriver.Driver | Yes |
PostgreSQL | postgresql | PostgreSQLProvider | PostgreSQLDriver.Driver | No |
MongoDB | N/A | MongoProvider | N/A | No |
对于大型数据库官方只有支持到MySQL,稍显遗憾,开发团队最近都在进行Vapor 3的开发,相信不久后就可以有更多的数据库类型支持了,而且由于Fluent的抽象的特性,只要有相应的驱动,适配任何数据库我想只是时间问题。
既然是抽象层,我们先不管用啥数据库,可以先把我们的数据模型搭建起来。
我想给我的网站加一段名人名言,于是我创建一个名为Quotes的模型,代码如下:
import Vapor
import FluentProvider
import HTTP
/// 名人名言
final class Quotes: Model {
// 这个属性能让Fluent存储额外的信息,如这个model的id
let storage = Storage()
//***下面是表中的属性***
/// 作者
let author: String
/// 内容
let content: String
/// 描述
let description: String
/// 数据库中列的名字
struct Keys {
static let id = "id"
static let author = "author"
static let content = "content"
static let description = "description"
}
// MARK: 初始化Fluent
/// 初始化Quotes
required init(row: Row) throws {
author = try row.get(Quotes.Keys.author)
content = try row.get(Quotes.Keys.content)
description = try row.get(Quotes.Keys.description)
}
// 序列化Quotes到数据库
func makeRow() throws -> Row {
var row = Row()
try row.set(Quotes.Keys.author, author)
try row.set(Quotes.Keys.content, content)
try row.set(Quotes.Keys.description, description)
return row
}
}
我们的model有了,下面就该联系一下数据库了,Fluent 提供了一个Preparation协议,源码如下:
/// A preparation prepares the database for
/// any task that it may need to perform during runtime.
public protocol Preparation {
/// The prepare method should call any methods
/// it needs on the database to prepare.
static func prepare(_ database: Database) throws
/// The revert method should undo any actions
/// caused by the prepare method.
///
/// If this is impossible, the `PreparationError.revertImpossible`
/// error should be thrown.
static func revert(_ database: Database) throws
}
其中prepare方法是让数据库做好准备的方法,比如新建table,而revert方法则是对prepare做的操作进行回滚操作,比如删除table。
另外,JSON也是网络通讯常用的数据格式,模型通常也需要转换为JSON串,或者需要解析json串到模型。JSON库为我们提供了JSONConvertible协议,demo如下
extension Quotes: JSONConvertible {
convenience init(json: JSON) throws {
self.init(
author: try json.get(Quotes.Keys.author),
content: try json.get(Quotes.Keys.content),
description: try json.get(Quotes.Keys.description)
)
}
func makeJSON() throws -> JSON {
var json = JSON()
try json.set(Quotes.Keys.id, id)
try json.set(Quotes.Keys.author, author)
try json.set(Quotes.Keys.content, content)
try json.set(Quotes.Keys.description, description)
return json
}
}
在写这个extension之前,还需要在mode里添加一个初始化方法:
/// 名人名言
final class Quotes: Model {
...
// MARK: 初始化Fluent
init(author: String, content: String, description: String) {
self.author = author
self.content = content
self.description = description
}
...
}
模型已经建好了,那么作为一个数据库模型,怎么能少了增删改查呢,药药药,切克闹,增删改查来一套:
这里我们需要开始写Controller了,在controller文件夹内创建一个QuotesController.swift的文件:
import Vapor
import FluentProvider
struct QuotesController {
func addRoutes(to drop: Droplet) {
let quots = drop.grouped("api","quots")
}
}
然后在Config+Setup.swift中准备好新创建的model:
private func setupPreparations() throws {
preparations.append(Quotes.self)
}
接下来在创建一个Routers+Quotes.swift的文件并添加QuotesController的routs.
Routers+Quotes.swift:
import Vapor
extension Droplet {
func setupQuotes() {
let quotsController = QuotesController()
quotsController.addRoutes(to: self)
}
}
最后在Droplet+Setup.swift中添加setupQuotes()方法:
@_exported import Vapor
extension Droplet {
public func setup() throws {
setupQuotes()
}
}
现在就可以在我们的controller里面写增删改查了:
import Vapor
import FluentProvider
struct QuotesController {
func addRoutes(to drop: Droplet) {
let quots = drop.grouped("api","quots")
//添加一个新的quots
quots.post("create", handler: createQuots)
//查询所有的quotes
quots.get(handler: allQuotes)
// 更新quotes
quots.post("update", handler: updateQuotes)
// 删除quotes
quots.post("delete", handler: deleteQuotes)
}
/// 添加一个新的quots
func createQuots(_ req: Request) throws -> ResponseRepresentable {
guard let json = req.json else {
throw Abort.badRequest
}
let quots = try Quotes(json: json)
try quots.save()
return quots
}
/// 查询所有的quots
func allQuotes(_ req: Request) throws -> ResponseRepresentable {
let quots = try Quotes.all()
return try quots.makeJSON()
}
/// 更新quotes
func updateQuotes(_ req: Request) throws -> ResponseRepresentable {
guard let json = req.json else {
throw Abort.badRequest
}
let id: Int = try json.get("id")
if let quots = try Quotes.find(id) {
try quots.update(json: json)
}
return try Quotes.all().makeJSON()
}
// 删除quotes
func deleteQuotes(_ req: Request) throws -> ResponseRepresentable {
guard let json = req.json else {
throw Abort.badRequest
}
let id: Int = try json.get("id")
if let quots = try Quotes.find(id) {
try quots.delete()
}
return try Quotes.all().makeJSON()
}
}
还需要在Quotes中加入一个update方法,并把参数改成var
/// 名人名言
final class Quotes: Model {
/// 作者
var author: String
/// 内容
var content: String
/// 描述
var description: String
...
}
extension Quotes {
func update(json: JSON) throws {
self.author = try json.get(Quotes.Keys.author)
self.content = try json.get(Quotes.Keys.content)
self.description = try json.get(Quotes.Keys.description)
try self.save()
}
}
现在我们的增删改查就已经完成了,下面cmd+r运行程序,用Rested测试接口:
增加一个名言
查询插入的结果
更新刚刚插入的数据
删除刚刚插入的数据
由于默认的数据库是基于内存加载的,重新运行程序则会清空,如果想要保存数据到服务器,你需要使用持续化的数据库,如MySQL、PostgreSQL以及MongoDB,后面我会对这几个数据库操作一一介绍。
关于Vapor其他知识,可以参考以下文章:
希望你对我的教程能够喜欢,你们的赞是我持续的动力,欢迎加入QQ群参与互动:431296189