Play Frameworkを使ってみる2

今日はPlay Frameworkを使って簡単なアプリケーションを作成してみます（今日だけで完成する訳ではありませんが）。簡単なアプリケーションとして単語帳を作成してみることにします。

# アプリケーションの作成

play new命令で新しいアプリケーションを作成します。アプリケーションのフォルダの名称は`tangotyou`とし、アプリケーション自体の名称も`tangotyou`とします。

```
c:\work\play>play new tangotyou
       _
 _ __ | | __ _ _  _
| '_ \| |/ _' | || |
|  __/|_|\____|\__ /
|_|            |__/

play 2.2.6 built with Scala 2.10.3 (running Java 1.8.0_60), http://www.playframe
work.com

The new application will be created in C:\work\play\tangotyou

What is the application name? [tangotyou]
> tangotyou

Which template do you want to use for this new application?

  1             - Create a simple Scala application
  2             - Create a simple Java application

> 1
OK, application tangotyou is created.

Have fun!
```

# アプリケーションの設定

`conf\application.conf`はアプリケーションの設定を記述するファイルです。アプリケーションの作成直後には次のようになっています。

```:conf\application.conf
# This is the main configuration file for the application.
# ~~~~~

# Secret key
# ~~~~~
# The secret key is used to secure cryptographics functions.
# If you deploy your application to several instances be sure to use the same key!
application.secret="E=Z=lhPk8@hfXgI?^N/oDAibk]oIxWRkvpwIZYw;n>edsqpb_NBlVPR>/Khfy:Yd"

# The application languages
# ~~~~~
application.langs="en"

# Global object class
# ~~~~~
# Define the Global object class for this application.
# Default to Global in the root package.
# application.global=Global

# Router
# ~~~~~
# Define the Router object to use for this application.
# This router will be looked up first when the application is starting up,
# so make sure this is the entry point.
# Furthermore, it's assumed your route file is named properly.
# So for an application router like `my.application.Router`,
# you may need to define a router file `conf/my.application.routes`.
# Default to Routes in the root package (and conf/routes)
# application.router=my.application.Routes

# Database configuration
# ~~~~~
# You can declare as many datasources as you want.
# By convention, the default datasource is named `default`
#
# db.default.driver=org.h2.Driver
# db.default.url="jdbc:h2:mem:play"
# db.default.user=sa
# db.default.password=""

# Evolutions
# ~~~~~
# You can disable evolutions if needed
# evolutionplugin=disabled

# Logger
# ~~~~~
# You can also configure logback (http://logback.qos.ch/),
# by providing an application-logger.xml file in the conf directory.

# Root logger:
logger.root=ERROR

# Logger used by the framework:
logger.play=INFO

# Logger provided to your application:
logger.application=DEBUG
```

既定の言語の設定を変えておきましょう。

`application.langs="en"`を`application.langs="ja"`に変更します。つまり、既定の言語を英語から日本語に変更します。

# 言語ファイル

多言語化のためには言語ファイルが必要です。既定の言語ファイルは`conf\messages`に保存します。また、それぞれの言語の言語ファイルは`conf\messages.＜ISO 639-1 言語コード＞`に保存します。たとえば、日本語の言語ファイルは`conf\messages.ja`に保存します。

取り敢えず、アプリケーションの名称の表記を既定の言語ファイルに追加しておきましょう。今回のアプリケーションの既定の言語は日本語ということにします。

```:conf\messages
application.name = 単語帳
```

# モデル

単語帳に最低限必要なモデルは単語です。単語の属性としてはレベルや品詞、意味などが挙げられます。

また、適当なサンプルデータを作成します。最終的には任意の単語を登録できるようにしなければならないでしょうが、取り敢えずはこのデータを使っていくことにします。

```scala:app\models\Tango.scala
package models

import java.util.UUID

sealed trait ImiSyurui {}
final case class Meisi() extends ImiSyurui
final case class Dousi() extends ImiSyurui
final case class Keiyousi() extends ImiSyurui
final case class Hukusi() extends ImiSyurui

case class Imi(syurui: ImiSyurui, imi: String)
case class Tango(id: UUID, name: String, level: Int, imis: List[Imi])

object Tango {
  val tangos = List(
    Tango(UUID.fromString("67f26c63-4d58-4297-9926-f4eaef8cb947"), "incur", 9, List(Imi(Dousi(), "～を招く"), Imi(Dousi(), "～を受ける"), Imi(Dousi(), "～を被る"), Imi(Dousi(), "～を負う"), Imi(Dousi(), "～を負担する"))),
    Tango(UUID.fromString("39b49fb2-1ca1-42c1-88d3-888c42c73029"), "coarsen", 13, List(Imi(Dousi(), "～を粗雑にする"))),
    Tango(UUID.fromString("619fea3d-135e-4859-9fcb-f9054a1bde36"), "engaging", 7, List(Imi(Keiyousi(), "人を引き付ける"))),
    Tango(UUID.fromString("83586638-6fb4-4682-a109-72d45df34ebd"), "credit", 3, List(Imi(Dousi(), "～を信用する"), Imi(Dousi(), "～を認める"), Imi(Dousi(), "～を貸方に記入する"), Imi(Dousi(), "～に単位を与える"), Imi(Meisi(), "信用取引"), Imi(Meisi(), "単位"), Imi(Meisi(), "貸方"))),
    Tango(UUID.fromString("e156d0bd-30e0-4c09-9dd6-614deaca5749"), "afford", 3, List(Imi(Dousi(), "～を買うことができる"), Imi(Dousi(), "～をすることができる"), Imi(Dousi(), "～を提供する"))),
    Tango(UUID.fromString("f13a53a1-c0c4-47e0-afe7-799dfb7575e9"), "passing", 4, List(Imi(Keiyousi(), "合格の"))),
    Tango(UUID.fromString("b07cb464-3cbc-4bb5-aad2-52036e9696b0"), "mark", 2, List(Imi(Dousi(), "～の印を付ける"), Imi(Dousi(), "～を指し示す"), Imi(Dousi(), "～を知らせる"), Imi(Dousi(), "～を特徴付ける"))),
    Tango(UUID.fromString("6a2b2d5c-ed15-447c-8564-e70811fb0471"), "follow-up", 13, List(Imi(Meisi(), "続報"))),
    Tango(UUID.fromString("2dae269f-c9fa-4893-bb15-8daa2bb90834"), "favorable", 4, List(Imi(Keiyousi(), "有利な"))),
    Tango(UUID.fromString("f2879078-6910-4b77-bb55-c8d5fa9daf56"), "tap", 3, List(Imi(Dousi(), "～を軽く叩く"), Imi(Dousi(), "～を指名する")))
  )
}
```

# テンプレート

テンプレートはHTMLファイルにScalaのコードを埋め込むことによって作成します。

単語の一覧を表示するためのテンプレートを作成します。

```scala:app\views\tangos\list.scala.html
@(tangos: List[Tango])(implicit lang: Lang)

@head(tango: Tango) = @{
    tango.name
}

@body(tango: Tango) = @{
    showLevel(tango.level) + " " + tango.imis.groupBy((x) => x.syurui).map((x) => "[" + showSyurui(x._1) + "]" + x._2.map((y) => y.imi).mkString("、")).mkString(" ")
}

@showSyurui(syurui: ImiSyurui) = @{
     syurui match {
        case x: Meisi => "<span class=\"meisi\">名</span>"
        case x: Dousi => "<span class=\"dousi\">動</span>"
        case x: Keiyousi => "<span class=\"keiyousi\">形</span>"
        case x: Hukusi => "<span class=\"hukusi\">副</span>"
    }
}

@showLevel(level: Int) = @{
    "<span class=\"level" + level + "\">" + level + "</span>"
}

@main(Messages("application.name")){
    <dl class="tangos">
        @for(tango <- tangos){
            <dt>@Html(head(tango))</dt>
            <dd>@Html(body(tango))</dd>
        }
    </dl>
}
```

ページを表示するためのテンプレートを作成します。

```scala:app\views\main.scala.html
@(title: String)(content: Html)(implicit lang: Lang)

<!DOCTYPE html>
<html>
    <head>
        <title>@title</title>
    </head>
    <body>
        <div class="title">
            <a href="@routes.Application.index()">
                @Messages("application.name")
            </a>
        </div>
        <div class="content">
            @content
        </div>
    </body>
</html>
```

# コントローラ

HTTPリクエストを受け取り、HTTPレスポンスを返すコントローラを追加します。単語の一覧を表示するためのテンプレートに単語の一覧を渡すことで単語の一覧を表示するページを生成します。

```scala:app\controllers\Tangos.scala
package controllers

import play.api.mvc.{Action, Controller}
import models.Tango

object Tangos extends Controller{
  def list = Action(implicit request =>
    Ok(views.html.tangos.list(Tango.tangos))
  )
}
```

# 経路

経路の設定を追加します。`conf\routes`に`GET     /tangos                     controllers.Tangos.list`を追加します。

```:conf\routes
# Routes
# This file defines all application routes (Higher priority routes first)
# ~~~~

# Home page
GET     /                           controllers.Application.index

# Map static resources from the /public folder to the /assets URL path
GET     /assets/*file               controllers.Assets.at(path="/public", file)


GET     /tangos                     controllers.Tangos.list
```

# 結果

これで単語の一覧を表示するページにアクセスできるようになりましたので、実際にアクセスしてみます。

![res.png](https://qiita-image-store.s3.amazonaws.com/0/10969/6079c4bf-3eb8-1ee1-4e7a-6c7f5d00c76a.png)

ページが適切に生成されていることが確認できました。