Vapor奇幻之旅(05 Fluent)

weixin_34321977發表於2018-01-23
1604032-81e6dab6938d81f8.png

在上一篇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測試介面:

1604032-f281f17440195a1e.png
增加一個名言

1604032-c3f5fb764a157e6a.png
查詢插入的結果
1604032-b733bc03da59e8f0.png
更新剛剛插入的資料
1604032-e44176bdbf7966ea.png
刪除剛剛插入的資料

由於預設的資料庫是基於記憶體載入的,重新執行程式則會清空,如果想要儲存資料到伺服器,你需要使用持續化的資料庫,如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

相關文章