DBから取得した値をViewに載せて返してもらう(Vapor4対応)

Leaf
DBから取得した値をViewに載せて返してもらう(Vapor4対応)

はじめに

Leafテンプレートをレンダリングして静的なページを返してもらうだけではなく、値を渡して動的にページを生成することもありえます。実用上は後者のほうが圧倒的に多いように思います。

パッと思いつくものといえば、データベースから取得したデータを一覧表示するとかですね。

POSTメソッドのときの構文と静的ページを取得するGETメソッドの書き方の組み合わせで対応してみたいと思います。もちろんVapor4です。

DBから取得した値をLeafテンプレートに載せてみる

データを全部取得した後、取得したものをLeafテンプレートに渡す事例で書き換えをしてみます。
なお、ObjectIDがあるので、MongoDBから取得したものを直接Leafテンプレートに渡してレンダリングすることができません。ですので、ループを回してレンダリングのための構造体に焼き直しをしています。

Vapor3のときはこのような感じにしていました。データの取得、焼き直し、レンダリングと順番に進めています。毎度のことですがtryの処理が雑ですね。

Vapor3
var books = [BookData]()
let booksDocs = try! collection.find()

for book in booksDocs {
    let bookData = BookData(id: book._id?.hex ?? "", title: book.title, publisher: book.publisher, price: book.price)
    books.append(bookData)
}
return try req.view().render("BookList", books)
}

Vapor4になっても基本的な流れは同じです。
非同期処理なことを考慮し、順次取得されたデータに対して処理をつなげていきます。
MongoDBから取得したデータを配列にまとめた後はVapor3の時と同様です。

Vapor4
var books = [BookData]()
return collection.find().flatMap { cursor in
    cursor.toArray()
}.flatMap { booksDocs -> EventLoopFuture<View> in
    for book in booksDocs {
        let bookData = BookData(id: book._id.hex, title: book.title, publisher: book.publisher, price: book.price)
        books.append(bookData)
    }
    return req.view.render("BookList", books)
}

渡されたデータを取り込んでLeafテンプレートがレンダリングされ、EventLoopFuture<View>が返されます

おまけ:あいまい検索もしたい

Vapor3のときは、あいまい検索をするためにこのようにしていました。

Vapor3
let booksDocs = try! collection.find().filter({ book -> Bool in
  return book.title.contains(title)
})

Vapor4ではなんとfilterが無いです。
ならばあいまい検索をどうやるかということですが、原始的な方法で実現できました。

Vapor4
for book in booksDocs {
  if book.title.contains(title) {
    let bookData = BookData(id: book._id.hex, title: book.title, publisher: book.publisher, price: book.price)
    books.append(bookData)
  }
}

指定した文字列がタイトルに含まれているかどうかで、表示させるべきデータとするかどうかを判定しています。
Vapor4フレームワークを使った王道は、findメソッドで正規表現による条件を指定することでしょう。手っ取り早く実現するには原始的な方法も有効ですよという一つの例でした。

まとめ

ページネーションなど他のバリエーションについては、基本は同じなので応用は利くと思います。それにしてもVapor3からVapor4への移行は骨が折れっぱなしです。