VaporアプリでMongoDBドキュメントをリストで表示してみる

Swift
VaporアプリでMongoDBドキュメントをリストで表示してみる

はじめに

vaporフレームワークを使ったWebアプリケーションに挑戦して、静的ページを表示することができました。

静的ページとくれば今度は動的に生成するページを試してみたくなるわけですよ。
というわけで、MongoDBのドキュメントをリスト形式で表示してみます。

ドキュメントを取得する

ドキュメントの取得は既存のものを使います。
MongoDBのドキュメント全部を取得するWebAPIです。

router.get("api/books") { req -> [Book] in
    let client = try! req.make(MongoClient.self)
    let collection = client.db("vaporapp").collection("books", withType: Book.self)

    var books = [Book]()

    let booksDocs = try! collection.find()
    for book in booksDocs {
       books.append(book)
    }
    return books
}

ドキュメント一式を配列で返すものですが、Leafテンプレートに表示させて返すように作り変えていきます。

LeafテンプレートをレンダリングしたViewを返す

Leaf RendererでレンダリングしたViewを返すようにします。

router.get("api/books") { req -> Future<View> in
    let client = try! req.make(MongoClient.self)
    let collection = client.db("vaporapp").collection("books", withType: Book.self)

    var books = [Book]()

    let booksDocs = try! collection.find()
    for book in booksDocs {
       books.append(book)
    }

    return try req.view().render("BookList", ["books":books])
}

本の情報をまとめた構造体Bookの配列[Book]を返していましたが、Viewを返すようFuture<View>にしました。

また、静的ページのときに倣って、try req.view().render("BookList", ["books":books])にしています。Leafテンプレートファイルの名前はBookList.leafにしました。安直。

Leafテンプレートに表示したい情報は、引数の2つ目にDictionary型で渡します。キーはLeafテンプレートで参照するキーワードになります。今の場合は、Leafテンプレート内でbooksと指定すると、渡したDictionaryから値を探して表示してくれます。ざっくりとしたイメージはこんな感じです。

参考:Book構造体

struct Book: Content {
    var title: String
    var publisher: String
    var price: Int
}

Leafテンプレートを作る

MongoDBから取得した全ドキュメントについて、タイトル、出版社、価格をテーブルに表示してみようと思います。

LeafテンプレートへDictionaryで渡した値を参照するためには#([Dictionaryのキー])を使います。先ほど["books":books]と渡したので、#(books)ですね。ただし、渡されるデータの中身は文字列でも数値でもなく、構造体Bookの配列なのでそのままでは何も表示されません。配列を一つずつ処理していく必要があります。

公式サイトでもSwift-inspired syntaxと謳われています。安心してください。ループ処理が用意されています。ループ処理は馴染みのあるforを使い、#for(book in books)という形で実現できます。

これで渡された配列から本のデータを一つずつ取り出して処理することができます。取り出したデータの中にタイトルや出版社名、価格が入っています。それらのデータどうやって取り出すかですが、#(book.[プロパティ名])で取り出すことができます。これも馴染みのある書き方で、妙な安心感があります。

ここまでくれば各値を表示していくだけです。最終的にテーブルにまとめてみました。
データを繰り返して処理するのと同時に、HTMLの繰り返しもまとめられます。

<!DOCTYPE html>
<html>
    <head>
        <title>Book List</title>
    </head>
    <body>
        <h1>Book List</h1>
        <table>
            #for(book in books) {
                <tr>
                    <td>#(book.title)</td>
                    <td>#(book.publisher)</td>
                    <td>#(book.price)</td>
                </tr>
            }
        </table>
    </body>
</html>

動かしてみよう

CSS周りをいじっていないスッピン状態なので大変素っ気ないですが、リスト表示ができていることが分かります。

ただの羅列ですが、ちゃんとリスト表示されています

ちょっと意地悪してみた

return try req.view().render("BookList", userInfo: ["books":books,"book":Book(title: "どう表示されるかな?", publisher: "意地悪出版", price: 7000)])

データを参照するキーの値bookを衝突させてみました。どう表示されるか?
名前衝突で処理できず、途中で止まるだろうなと予想。

リストが表示されない

想定通りの動きでした。
名前衝突しないように気をつけるかルールづくりが必要ですね。

まとめ

リスト表示が無事できるようになりました。Leafテンプレートには#forのループ以外にももちろん条件分岐の#ifもありますし、演算もできます。渡されるデータが何かということさえはっきりさせておけば、フロントエンドとバックエンドの役割分担がやりやすそうですね。

ところで、Leafテンプレートのレンダリングデバッグはどこで見られるんだろうか?Chromeのデベロッパツール?IDEに頼ってみる必要があるかもしれないです。