環境
バージョン | |
---|---|
Swift | DEVELOPMENT-SNAPSHOT-2016-07-25-a |
Xcode | 8.0 beta 3 (8S174q) |
Mac | OSX 10.11.5(15F34) |
Vapor | v0.16.0 |
Vapor Toolbox | v0.8.0 |
全てMacのローカル環境で行っています。
Vaporのプロジェクトを作成
今回は、公式DocsのサンプルアプリケーションにMySQL接続部分を追加してみたいので、
以下を参考にHello
プロジェクトを作成します。
https://vapor.github.io/documentation/
Docsの通りに行えば問題なくできるかと思いますので、ここの詳細は省きます。
「GETTING STARTED」の「Hello, World」のセクションで、ローカルでサーバーを立てることができればOKです。
MySQLを起動
次に、あらかじめローカル環境でMySQLを起動させておきます。
こちらを参考に、まずはmacにMySQLをインストールし、ローカルで起動させます。
brew install mysql
brew link mysql
mysql.server start
MySQLにデータベースを作成
MySQLにデータベースをあらかじめ用意しておきます。
今回はvapor
という名前のデータベースを作成します。
データベースのconfigファイルを作成
プロジェクト直下のConfigフォルダ内に自分のMySQLの環境に合わせて次のような設定ファイルをmysql.json
という名前で作成します。
{
"host": "localhost",
"user": "root",
"password": "",
"database": "vapor"
}
プロバイダーの追加
https://vapor.github.io/documentation/guide/provider.html
上記を参考に、mysql-provider
をプロジェクトに追加します。
Package.swift
にパッケージを追加する
Package.swift
に
.Package(url: "https://github.com/vapor/mysql-provider.git", majorVersion: 0, minor: 4)
を追加します。
import PackageDescription
let package = Package(
name: "Hello",
dependencies: [
.Package(url: "https://github.com/vapor/vapor.git", majorVersion: 0, minor: 16),
.Package(url: "https://github.com/vapor/vapor-mustache.git", majorVersion: 0, minor: 11),
.Package(url: "https://github.com/vapor/mysql-provider.git", majorVersion: 0, minor: 4)
],
exclude: [
"Config",
"Database",
"Localization",
"Public",
"Resources",
"Tests",
]
)
Dropletにプロバイダーを追加
main.swift
に次のようにMySQLのプロバイダーを追加します。
import Vapor
import VaporMustache
import VaporMySQL // ←追加
import HTTP
/**
Droplets are service containers that make accessing
all of Vapor's features easy. Just call
`drop.serve()` to serve your application
or `drop.client()` to create a client for
request data from other servers.
*/
let drop = Droplet(providers: [VaporMustache.Provider.self, VaporMySQL.Provider.self])
// ↑VaporMySQL.Provider.selfを追加
// 以下略
import VaporMySQL
とDropletの引数のprovidersにVaporMySQL.Provider.self
を追加しました。
いったん動かしてみる
ここまでで、いったんビルドして、サーバーを動かしてみます。
プロジェクト直下で以下のようにビルドします。
swift build -Xswiftc -I/usr/local/include/mysql -Xlinker -L/usr/local/lib
以降、上記コマンドでビルドします。
<追記:2016/08/16>
vapor build --mysql
このコマンドでもビルドできるようなので、こちらのほうが便利ですね。
<追記ここまで>
次に、vapor run
でサーバーを立ち上げます。
上手く立ち上がれば、いったんOKです。
ちなみに、、
vapor build
や単純にswift build
だとmysqlのライブラリが見つからないと怒られてしまいました。↓
Linking ./.build/debug/App
ld: library not found for -lmysqlclient for architecture x86_64
<unknown>:0: error: link command failed with exit code 1 (use -v to see invocation)
モデルをMySQLと紐付ける
モデルにテーブルの作成・削除ロジックを追加する
models/User.swift
に3点追記します。
import Vapor
import Fluent
final class User: Model {
var id: Node?
var name: String
init(name: String) {
self.name = name
}
init(node: Node, in context: Context) throws {
id = try node.extract("id")
name = try node.extract("name")
}
func makeNode() throws -> Node {
return try Node(node: [
"id": id, // ←追加! (1)
"name": name
])
}
static func prepare(_ database: Database) throws {
// ↓追加! (2)
try database.create("users") { users in
users.id()
users.string("name")
}
}
static func revert(_ database: Database) throws {
// ↓追加! (3)
try database.delete("users")
}
}
- 追加1:Nodeについてはまだきちんと分かっていないのですが、Docsだとidも記述されていたので、足しています。(どちらが正しいのか、、)
-
追加2:これを追加し、後述のDropletへの追記をすることで、
vapor run
した時に、MySQLへusers
テーブルが作られます。 -
追加3:こちらは、
vapor run prepare --revert
と打つことで呼ばれ、users
テーブルを削除します。(手元でこの挙動を確認できませんでした。。)
Dropletにpreparationsを追加する
main.swift
のDropletの初期化部分にpreparations
を追加します。
これがないと、先ほどモデルに書いたprepare
メソッドが呼ばれず、テーブルの作成がされません。
import Vapor
import VaporMustache
import VaporMySQL
import HTTP
/**
Droplets are service containers that make accessing
all of Vapor's features easy. Just call
`drop.serve()` to serve your application
or `drop.client()` to create a client for
request data from other servers.
*/
let drop = Droplet(
preparations: [User.self], // ←追加
providers: [VaporMustache.Provider.self, VaporMySQL.Provider.self]
)
// 以下略
余談ですが、引数の順序として、providers
よりpreparations
の方が先に記述しないと怒られます。
これで、前述のコマンドでビルドし、
vapor run
でサーバー立ち上げます。
$ vapor run
Running Hello...
No command supplied, defaulting to serve...
Preparing User
Prepared 'User'
Database prepared
こんな感じでusersテーブルが作成されます。
mysql> use vapor
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A
Database changed
mysql> show tables;
+-----------------+
| Tables_in_vapor |
+-----------------+
| fluent |
| users |
+-----------------+
2 rows in set (0.00 sec)
mysql> show columns from users;
+-------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------+--------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| name | varchar(255) | NO | | NULL | |
+-------+--------------+------+-----+---------+----------------+
2 rows in set (0.00 sec)
データを追加する
とりあえず実用性は考えずに、クエリをつけてPOSTすればデータを追加できるようなものを作ってみます。
http://localhost:8080/users
に対して、POST
をすると、
Controllers/UserController.swift
のstore
メソッドが呼ばれます。
(vapor newで作られるデフォルトのアプリケーションだとそうなっているので、それをそのまま使います。)
今回は手っ取り早くstore
メソッド内に、データの保存を行うコードを追加します。
// 略
func store(request: Request) throws -> ResponseRepresentable {
// 追加部分
if let name = request.data["name"].string {
user = User(name: name)
try user.save()
}
// 追加部分ここまで
return try JSON([
"controller": "UserController.store"
])
}
// 略
nameというパラメータを受け取って、Userモデルのインスタンスを作成し、保存しています。
これで再度ビルドし、vapor run
でサーバーを立ち上げます。
それでは、実際にPOSTをしてみましょう。
方法はなんでもいいですが、curlで行う場合は、次のような感じです。
curl http://localhost:8080/users -X POST -d "name=hoge"
これで、データが保存されていればOKです。
データを削除する
こちらもまずは実用性は考えずに、単純にDELETEメソッドでリクエストするとデータが削除されるものを作ります。
http://localhost:8080/usres/{id}
にDELETEメソッドでリクエストすると、Controllers/UserController.swift
のdestroy
メソッドが呼ばれます。
(vapor newで作られるデフォルトのアプリケーションだとそうなっているので、そのまま使います。)
このdestroy
メソッドにデータを削除するコードを追加します。
// 略
func destroy(request: Request, item user: User) throws -> ResponseRepresentable {
//User is ResponseRepresentable by proxy of JsonRepresentable
try user.delete() // ←追加
return user
}
// 略
こちらも、curlでDELETEを投げてみると、データが削除されると思います。
↓ID:1のデータを削除
curl http://localhost:8080/users/1 -X DELETE
データを更新する
データの更新については、正しいやり方ではない気しかしないですが、とりあえずできたやり方を残します。
ここまでの流れから分かるかと思いますが、更新はPUT
メソッドでのリクエストでControllers/UserController.swift
のupdate
メソッドが呼ばれます。
// 略
func update(request: Request, item user: User) throws -> ResponseRepresentable {
if let name = request.data["name"].string {
var user = try User(node: user.makeNode())
user.name = name
try user.save()
return user.makeJSON()
}
return user.makeJSON()
}
// 略
ちょっと回りくどいですね。。
「とりあえず感」がすごいですが、これでcurlでPUTを送ると一応データの更新ができます。
curl http://localhost:8080/users/1 -X PUT -d "name=fuga"
ただ、vaporのコンソールを見ていると、
Server error: dispatch(HTTP.ParserError.streamEmpty)
というエラーが出ているので、やはり何か違うのでしょう、、
引き続きコードやDocs読んで試していきます。
モデルのデータ一覧取得
もはやどこでもいいのですが、とりあえずhttp://localhost:8080/users
にGET
でのアクセス(ブラウザでアクセス)して一覧をjsonで表示させるようなものにしました。
func index(request: Request) throws -> ResponseRepresentable {
let users = try User.query().all()
return try JSON(users)
}
try User.query().all()
で取得できるというところが分かれば、使いどころはここでなくても大丈夫です。
Fluent
ここまで、MySQLへのデータの操作を簡単に見てきましたが、データの操作にはFluentが使われています。
FluentはSwift製のORMです。
これまでの使い方以外にもfilter
やrelation
なども扱えるので、いろいろ触ってみると面白そうです。
https://github.com/vapor/fluent
https://vapor.github.io/documentation/fluent/query.html
終わりに
今回は、クイックにMySQLとの接続を試してみたかったので、書き方や細かいところは全く考えていません。
というより、単純に分からないところが多いというのが実際のところなので、知見のある方、是非ご教授願いたいです
また、Docsがかなり分かりやすい(分かりやすくなってきた)ので、これからいろいろやってみたいと思います。