LoginSignup
22
22

More than 5 years have passed since last update.

【サーバーサイドSwift】VaporでMySQLと接続してみた【Vapor v0.16.0】

Last updated at Posted at 2016-08-06

環境

バージョン
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という名前で作成します。

Config/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)

を追加します。

Package.swift
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のプロバイダーを追加します。

main.swift
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点追記します。

models/User.swift
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メソッドが呼ばれず、テーブルの作成がされません。

main.swift
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.swiftstoreメソッドが呼ばれます。
(vapor newで作られるデフォルトのアプリケーションだとそうなっているので、それをそのまま使います。)

今回は手っ取り早くstoreメソッド内に、データの保存を行うコードを追加します。

Controllers/UserController.swift
// 略

    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.swiftdestroyメソッドが呼ばれます。
(vapor newで作られるデフォルトのアプリケーションだとそうなっているので、そのまま使います。)

このdestroyメソッドにデータを削除するコードを追加します。

Controllers/UserController.swift
// 略
    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.swiftupdateメソッドが呼ばれます。

Controllers/UserController.swift
// 略
    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/usersGETでのアクセス(ブラウザでアクセス)して一覧をjsonで表示させるようなものにしました。

Controllers/UserController.swift
    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です。

これまでの使い方以外にもfilterrelationなども扱えるので、いろいろ触ってみると面白そうです。

https://github.com/vapor/fluent
https://vapor.github.io/documentation/fluent/query.html

終わりに

今回は、クイックにMySQLとの接続を試してみたかったので、書き方や細かいところは全く考えていません。
というより、単純に分からないところが多いというのが実際のところなので、知見のある方、是非ご教授願いたいです :raised_hands:

また、Docsがかなり分かりやすい(分かりやすくなってきた)ので、これからいろいろやってみたいと思います。

22
22
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
22
22