はじめに
初めて出会ったFuture
に面食らって足踏みをしてしまいましたが、気を取り直して進んでいきます。
いよいよMongoかと思わせておいて、SQLiteです。焦らしているわけではないですが、少しずつ深みにハマっていく方向で。泳ぎでもまずは水に慣れて浮き輪をつけてとかやるではないですか?あれと同じ理屈です。
パッケージの導入
VaporでSQLiteを使うにはパッケージを導入する必要があります。なんと!ありがたいことに最初の状態で導入されています。
Package.swift
のDependencies
に
.package(url: "https://github.com/vapor/fluent-sqlite.git", from: "3.0.0")
があればOKです。
VaporでのSQLiteはfluent-sqlite
というのですね〜へぇ〜:D
データモデルを作る
データモデルを作っていきます。今回もJSONデータを扱いたいのでContent
プロトコルに準拠した構造体かクラスを作ります。ただし、今回はSQLiteを使うので、importとextensionが少々必要です。
import Vapor
import FluentSQLite
struct Book: Content {
var id:Int?
var title: String
var publish: String
var price: Int
init(title:String, publish:String, price:Int) {
self.title = title
self.publish = publish
self.price = price
}
}
extension Book:SQLiteModel {
static let entity: String = "books"
}
extension Book:Migration {
}
Migration
プロトコルとSQLiteModel
プロトコルが居ますが、「とりあえず準拠しました」的な感じで申し訳程度に添えています。荒々しく粗っぽい理解ですが、Migration
プロトコルについてはモデルが変わったりしたときに使うんでしょう。多分。Realmでよく見るマイグレーションと似た感じと理解しています。今はいらないでしょう多分。
SQLiteModel
プロトコルはその名前の通りSQLiteで扱うデータモデルを表すものだと思います。上記のコードでは空っぽですが、Content
プロトコルのくくりに入れているプロパティ群を入れると結局のところ、SQLiteModel
のプロパティを決めていることになります。SQLiteを使っていなかったこれまでの流れ上、Content
プロトコルのくくりに入れておくほうがわかりやすいなと思った次第です。extensionで別れているのでややこしくなっているだけです。説明もややこしい…… 伝わるかな?
entitiy
はデータベースで使うエンティティ名です(小並感)。どうでもいいですが、エンティティと入力したつもりが、エンチチィになっていることがあります。これから見かけるかもしれませんが、チチィはサラッと流しておいてください。
SQLiteへ接続するための設定
これまたありがたいことに、初期状態でSQLiteに接続するための設定を行うメソッドが用意されています。これを少し修正します。
データの保存先を変更する
let sqlite = try SQLiteDatabase(storage: .memory)
SQLiteはファイルベースのデータベースです。ですが、初期状態ではメモリ上に展開することになっています。このままではアプリケーションをRunして動いている間はメモリ上にデータが保持されていますが、終了してしまえばデータが消えてしまいます。
なので、.memory
を.file(path: "book.sqlite")
に書き換えます。ちなみにbook.sqliteになっていますが、任意のファイル名で構いません。
マイグレーションの設定を加える(直訳)
var migrations = MigrationConfig()
migrations.add(model: Book.self, database: DatabaseIdentifier<Book.Database>.sqlite)
services.register(migrations)
これはなんでしょうかね?
見た感じですが、ここでフレームワーク側にマイグレーションをよしなにしてねとお願いしているようですね。BookはMigration
プロトコルに準拠させているので、必要に応じてそのプロトコルで設定したマイグレーションを実行してくれるのでしょうおそらく。
ところで、トリガーは一体なんでしょうか?Realmのときはバージョンを指定して自動的にマイグレーションしてくれましたが。必要なときに調べてみます。
新しい本の情報を追加してみる
この段階で実行してみると、スッピンのデータベースファイルができました。なんてお手軽。早速、本のデータを追加してみます。
router.post(Book.self, at: "api/book") { (req, book) -> Future<Book> in
return book.save(on: req)
}
BookがSQLiteModel
プロトコルに準拠しているため、何もしなくてもsave
メソッドが使えます。このRouteにJSONをPOSTするOnlyです。にじみ出るルーさん感。
ここでPostmanの出番です。グダグダ書くより見たほうが早いですね。
HeaderにはJSONを送りますよということを記述しておきます。JSON本体には送りたいデータを記述します。必要な情報が記載できればプロトコルをPOSTにしてSendボタンをクリックするだけです。
あれ?id
は?
大丈夫です。FluentSQLiteがよしなに割り当ててくれます。親切。試しにもう一冊追加してみます。本当によしなにしてくれるならばid
には2が割り当てられるはずです。
素晴らしい!
SQLiteデータベースファイルの中身を見てみると、ちゃんとデータがあるのがわかります。
参考までに実験
SQLiteのデータベースファイルの中身を見てみると、何もしていないのにid
がPRIMARY KEY
になっています。FluentSQLiteが裏でよしなにやってくれるのはありがたいのですが、何処まで親切にやってくれるのか若干不安になってきます。そこで少し実験してみました。
id以外の名前にしてみる
var bookCode:Int?
結果はダメでした。なんとSQLiteModel
プロトコルに準拠していないというエラーが表示されました。id
以外の名前は受け付けてくれないみたいです。
Type '(作ったモデルの名称)' does not conform to protocol 'SQLiteModel'
idを他の値の中に埋めてみる
今度は順序を変えてvar id:Int?
を他のプロパティの中に埋めてみます。
これは特段問題が無いみたいです。要するにプロパティの中にid
というInt?
型のものがあれば良いようです。
int?以外にしてみる
Type '(作ったモデルの名称)' does not conform to protocol 'SQLiteModel'
デスヨネー
プロトコルとそれに対応するPRIMARY KEY
の型があるようです。
プロトコル | PRIMARY KEYの型 |
---|---|
SQLiteModel | Int? |
SQLiteStringModel | String? |
SQLiteUUIDModel | UUID? |
ちなみに、どのプロトコルにしてもPRIMARY KEY
となるプロパティはid
にしないといけません。
まとめ
まずはSQLiteデータベースにデータを投入することができました。コマンドラインから中身の確認ができたので今度はAPIを発行して取得してみます。取得できたらデータの更新と削除で一段落ですね。
いくつか気になるところがありますが、ひとまず必要に応じて調べることにして先に進んでいきましょう。初めてのことや新しいことを始めるのは骨が折れますが、その分楽しいですね〜