[Vapor3]MySQLを使う
[09/13, 2018] |
Vapor3でMySQLを使って、いていろ苦労したのでチートシート的に残します。
とくに、複数のクエリを同時に扱ったり、複数のオブジェクトを同時に保存したりする方法が全然わからず、すごい時間を使いました😭
1.パッケージの追加
Package.swiftにMySQLのパッケージを追加します。
Package.swift
let package = Package(
name: "Vapor3Practice",
dependencies: [
.package(url: "https://github.com/vapor/vapor.git", from: "3.0.0"),
.package(url: "https://github.com/vapor/fluent-mysql.git", from: "3.0.0")
],
targets: [
.target(name: "App", dependencies: ["FluentMySQL", "Vapor"]),
.target(name: "Run", dependencies: ["App"]),
.testTarget(name: "AppTests", dependencies: ["App"])
]
)
リポジトリとターゲットの指定
追加したのは下記の2行です。
.package(url: "https://github.com/vapor/fluent-mysql.git", from: "3.0.0")
.target(name: "App", dependencies: ["FluentMySQL", "Vapor"]),
パッケージを更新したら、
vapor update
をして、パッケージをインストールしましょう。
2.Modelの作成
User.swiftを作成して、UserのModelを作る
final class User : Model {
var id: Int?
var name: String
var email: String
var passwordHash: String
}
extension User : MySQLModel {}
extension User : Migration { }
extensionに MySQLModelをつけることで、MySQLを扱えるようにする。
extension User : MySQLModel {}
IDがInt以外の場合は、「MySQLStringModel」「MySQLUUIDModel」も用意されているみたいです。
3.ConfigでDBの設定をする
configure.swiftのconfigure(...)でデータベースの設定とMigrationを登録をする
public func configure(_ config: inout Config, _ env: inout Environment, _ services: inout Services) throws {
//FluentMySQLProviderの登録
try services.register(FluentMySQLProvider())
//DBの設定
let mySQLConfig : MySQLDatabaseConfig = MySQLDatabaseConfig(hostname: "127.0.0.1", port:3306, username: "root", password: "password", database: "database")
services.register(mySQLConfig)
//Migrationの登録
var migrations = MigrationConfig()
migrations.add(model: User.self, database: .mysql)
services.register(migrations)
}
MySQLDatabaseConfigでDBのhostやユーザーを指定できる。ここで指定したdatabseとユーザーは事前に作成しておく必要があります。
ここまでで、MySQLに繋がるようになります。
vapor runして下記が表示されればOK。
[ INFO ] Migrating 'mysql' database (....)
[ INFO ] Migrations complete (....)
4.各種クエリ
Create
let user = User(name: "name", email: "email", passwordHash: "ABC")
user.create(on: req)
router.post("addUser") { req in
let user = User(name: "name", email: "email", passwordHash: "ABC")
return user.create(on: req)
}
Select
モデルのquery(on: req)に続けて取得条件をつけて、取得します。
User.query(on: req).all()
- all()
- selectした結果の全てを取得
- first()
- selectした結果の1番最初の要素を取得
- chunk(max: 12)
- selectした結果の1番目から12番目までを取得(ただし結果が12未満の場合は全部)
filter で条件をつけて絞ることができる。
router.get("user") { req in
return User.query(on: req).filter(\User.email == "email").all()
}
Update
user.update(on: req)
router.patch("user") { req in
return user.update(on: req)
}
Delete
userには全てのデータが入っている必要はなく、削除したいidの入ったインスタンスを用意すれば削除できる。
let user = User()
user.id = 12 //削除したいオブジェクトのidを指定する
user.delete(on: req)
router.delete("user") { req in
return user.delete(on: req)
}
Where
whereはfliterを使うことで表現できる。FluentMySQLをインポートしないと、ビルドエラーになる。
import FluentMySQL
Userのnameカラムが"name"のuserを取得する例。
User.query(on: conn).filter(\User.name == "name").all()
column = value
「==」を変えることで条件を指定できる。
.filter(\User.name == "name") // column = value
.filter(\User.name != "name") // column != value
.filter(\User.name >= "name") // column >= value
//and so on...
In句
In句は「~~」で表現します。
.filter(\User.role ~~ [UserRole.admin, UserRole.writer,])
Limit
Limitはrangeで指定します。(内部的にLimitなのかはわかりませんw)
.range(20..<31) // selectした結果の20番目から30番目を取得する例
Order by
Order byはsortで指定できます。
.sort(\User.id, .descending)
Joinなど
Joiなどは利用しているんですが、まだいまいち使いこなせていないので、もしもう少し理解できたら追記します。
5.逆引き辞典w
取得(Select)したオブジェクトの取り出し方
Selectしたarticleをテンプレートに渡して表示する例。
router.get("user") { req in
return Article.query(on: conn)
.filter(\Article.status == Status.published).all().flatMap(to:View.self) {
articles in
//ここで何かする
return req.view().render("templatePath", articles)
}
}
複数のオブジェクトを保存する
「model.create(on: req)」の部分を変更すれば、updateやdeleteもできます。
router.post("addUsers") { req in
var models : [User] = []
models.append(User(name: "user1", email: "email", passwordHash: "ABC"))
models.append(User(name: "user2", email: "email", passwordHash: "ABC"))
return models.map{
model -> Future<User> in
return model.create(on: req)
}.flatten(on: req).flatMap(to: View.self) {
results in
// resultsには[Articleが入っている]
return req.view().render("templatePath", results)
}
}
クエリを2個発行して結果を利用する
query()を変数に入れてflatMapで展開して、結果を利用することができます。flatMapは最大4つまでEventLoopFuture<>を展開できるようです。
router.get("allitems") { req in
let alllArticles : EventLoopFuture<[Article]> = Article.query(on: req).all()
let allUsers : EventLoopFuture<[User]> = User.query(on: req).all()
flatMap(to: View.self, allArticles, allUsers) {
articles, users in
//ここで何かする
return req.view().render("templatePath", results)
}
}