Bootstrapを使うための準備

sbt newコマンドで作成されたプロジェクトにはデフォルトのレイアウトテンプレートとしてapp/views/main.scala.htmlが生成されています。ここにBootstrapで使用するCSSとJavaScriptを追加します。

@(title: String)(content: Html)

<!DOCTYPE html>

<html>
  <head>
    <title>@title</title>
    <link rel="stylesheet" media="screen" href="@routes.Assets.versioned("stylesheets/main.css")">
    <link rel="shortcut icon" type="image/png" href="@routes.Assets.versioned("images/favicon.png")">
    <script src="@routes.Assets.versioned("javascripts/hello.js")" type="text/javascript"></script>
    @* ↓↓↓↓ここから追加↓↓↓↓ *@
    <link rel="stylesheet" media="screen" href="//netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap-theme.min.css">
    <link rel="stylesheet" media="screen" href="//netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.min.css">
    <script src="//netdna.bootstrapcdn.com/bootstrap/3.1.1/js/bootstrap.min.js" type="text/javascript"></script>
    @* ↑↑↑↑ここまで追加↑↑↑↑ *@
  </head>
  <body>
    @content
  </body>
</html>

また、デフォルトではContent-Security-Policyヘッダがdefault-src 'self'を返すため上記で指定した外部CDNのCSSファイルやJavaScriptファイルを読み込むことができません。そこでconf/application.confに以下の設定を追加してContent-Security-Policyヘッダが出力されないようにしておきます。

play.filters.headers.contentSecurityPolicy=null

コントローラの雛形を作る

controllersパッケージにUserControllerクラスを以下のように作成します。

package controllers

import play.api.mvc._
import play.api.data._
import play.api.data.Forms._
import play.api.libs.concurrent.Execution.Implicits.defaultContext
import play.api.i18n.{MessagesApi, I18nSupport}
import play.api.db.slick._
import slick.driver.JdbcProfile
import models.Tables._
import javax.inject.Inject
import scala.concurrent.Future
import slick.driver.H2Driver.api._

class UserController @Inject()(val dbConfigProvider: DatabaseConfigProvider,
                               val messagesApi: MessagesApi) extends Controller
    with HasDatabaseConfigProvider[JdbcProfile] with I18nSupport {

  /**
   * 一覧表示
   */
  def list = TODO

  /**
   * 編集画面表示
   */
  def edit(id: Option[Long]) = TODO

  /**
   * 登録実行
   */
  def create = TODO

  /**
   * 更新実行
   */
  def update = TODO

  /**
   * 削除実行
   */
  def remove(id: Long) = TODO

}

Play 2.3まではコントローラはオブジェクトとして実装する必要がありましたが、Play 2.4ではクラスとして実装します。コンストラクタに@Injectアノテーションと2つの引数が定義されていますが、これはPlay 2.4から導入されたDI機能を使用するためのものです。

上記のコントローラではDI機能を以下のような目的で使用しています。

  • DatabaseConfigProvider … コントローラ内でデータベースアクセスを行うため
  • MessagesApi … Playの国際化機能を使用するため(本ハンズオンで作成するアプリケーションでは国際化機能は使用しませんが、後述するテンプレート内で使用するヘルパーがMessagesApiのインスタンスを必要とするため)

また、実際にコントローラ内でデータベースアクセスや国際化機能を利用するためにはDIで上記のインスタンスを取得するだけでなく、それぞれHasDatabaseConfigProviderトレイト、I18nSupportトレイトをミックスインする必要があります。

POINT

  • Play 2.5ではコントローラはクラスとして実装します
  • @InjectはDIのためのアノテーションです
  • データベースアクセスを行うにはコントローラにDatabaseConfigProviderをDIし、HasDatabaseConfigProviderトレイトをミックスインします
  • 国際化機能を使用するにはコントローラにMessagesApiをDIし、I18nSupportトレイトをミックスインします
  • TODOメソッドはAction not implemented yet.という501 NOT_IMPLEMENTEDレスポンスを返します

ルーティングの設定

クライアントから送信されたリクエストは、conf/routesの設定に従ってコントローラのメソッドへルーティングされます。 以下の設定を追記します。

# Mapping to /user/list
GET     /user/list                  controllers.UserController.list
# Mapping to /user/edit or /user/edit?id=<number>
GET     /user/edit                  controllers.UserController.edit(id: Option[Long] ?= None)
# Mapping to /user/create
POST    /user/create                controllers.UserController.create
# Mapping to /user/update
POST    /user/update                controllers.UserController.update
# Mapping to /user/remove/<number>
POST    /user/remove/:id            controllers.UserController.remove(id: Long)

POINT

  • マッピング定義で引数の型を省略すると、Stringになります
  • routesのコメントに日本語を記述するとコンパイルエラーになることがあります