Vapor奇幻之旅(05 Fluent)

2018-08-22 14:17:52 浏览数 (1)

在上一篇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的模型,代码如下:

代码语言:javascript复制
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协议,源码如下:

代码语言:javascript复制
/// 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如下

代码语言:javascript复制
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里添加一个初始化方法:

代码语言:javascript复制
/// 名人名言
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的文件:

代码语言:javascript复制
import Vapor
import FluentProvider

struct QuotesController {
    
    func addRoutes(to drop: Droplet) {
        let quots = drop.grouped("api","quots")
    }
}

然后在Config Setup.swift中准备好新创建的model:

代码语言:javascript复制
private func setupPreparations() throws {
        preparations.append(Quotes.self)
}

接下来在创建一个Routers Quotes.swift的文件并添加QuotesController的routs.

Routers Quotes.swift:

代码语言:javascript复制
import Vapor

extension Droplet {
    
    func setupQuotes() {
        let quotsController = QuotesController()
        quotsController.addRoutes(to: self)
    }
    
}

最后在Droplet Setup.swift中添加setupQuotes()方法:

代码语言:javascript复制
@_exported import Vapor

extension Droplet {
    public func setup() throws {
        setupQuotes()        
    }
}

现在就可以在我们的controller里面写增删改查了:

代码语言:javascript复制
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

代码语言:javascript复制
/// 名人名言
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其他知识,可以参考以下文章:

Vapor奇幻之旅(01开始)

Vapor奇幻之旅(02部署)

Vapor奇幻之旅(03上手)

Vapor奇幻之旅(04Routing)

Vapor奇幻之旅(05 Fluent)

Vapor奇幻之旅(06 PostgreSQL)

Vapor奇幻之旅(07 连接服务端PostgreSQL)

Vapor奇幻之旅(08 连接服务端MongoDB)

Vapor奇幻之旅(09 连接MySQL)

希望你对我的教程能够喜欢,你们的赞是我持续的动力,欢迎加入QQ群参与互动:431296189

0 人点赞