Playframework 2.0(Scala)でサンプルアプリケーション作成-2.ユーザー登録実装

2012.04.22

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

yabeライクなサンプルアプリケーションの作成&訂正

前回の記事ではyabeっぽいサンプルアプリケーション開発のための下準備を行いました。
今回は引き続き、ユーザー情報を登録するための機能を実装してみましょう。
その前に1つ訂正です。前回はScalaQueryを使用するためにライブラリ等を設定しましたが、DBアクセスにはPlay標準のAnormを使用します。

今回使用した動作環境は以下のとおりです。

  • OS : MacOS X 10.7.3
  • Java : 1.6.0_31
  • Scala : 2.9.1-1
  • Playframework : 2.0
  • MySQL : 5.5

ユーザー登録機能の実装

では順番に、シンプルなユーザー登録の機能を実装をしていきます。

モデルとコントローラの実装

まずはmodelsパッケージに、ユーザーを表すUserモデルを定義します。(User.scalaを作成)

package models
import anorm._
 /** Userテーブル用モデル. */
case class User(id: Pk[Long], name: String, email: String, password: String)

Userテーブルと対応させるモデルをケースクラスで定義しました。テーブルと同じく、idと名前、メール、パスワードのフィールドを持っています。
※まだDBアクセスの処理はここでは記述しません

次にUserControllerを修正しましょう。登録画面から入力された値をそのまま完了画面でかえすだけですが、
Playframeworkのフォーム機能を使用してみます。
このあたりを参照

package controllers

import play.api._
import play.api.db._
import play.api.mvc._
import play.api.data._
import play.api.data.Forms._
import models.User
import anorm.NotAssigned

object UserController extends Controller {

  val userForm: Form[User] = Form(
    // Userフォームマッピング
    mapping(
      "name" -> text(minLength = 4),
      "email" -> email,
      "password" -> tuple("main" -> text(minLength = 6), "confirm" -> text).verifying(
        // パスワードの入力ルール定義
        "Passwords don't match", passwords => passwords._1 == passwords._2)) {
        // Userフォームバインド
        (name, email, passwords) => User(NotAssigned, name, email, passwords._1)
      } {
        // Usetフォームアンバインド
        user => Some(user.name, user.email, (user.password, ""))
      }.verifying(
        "This username is not available",
        user => !Seq("admin", "guest").contains(user.name)))

  /**
   * ユーザー登録画面初期処理
   */
  def initEntry = Action { implicit request =>
    Ok(views.html.entry(userForm))
  }

  /**
   * ユーザー登録処理.(入力エラーがなければそのまま返す)
   */
  def submitEntry = Action { implicit request =>
    userForm.bindFromRequest.fold(
      errors => BadRequest(views.html.entry(errors)),
      user => {
        Ok(views.html.result(user))
      })
  }
}

コントローラーで定義しているuserForm(play.api.data.Form)は、画面からの入力フォームをマッピングして、Userモデルへ変換しています。
mapping関数を使用し、フォーム要素の分解と構築を定義しています。
また、入力ルールの定義も行なっています。nameは文字列で最小4文字の制約が課せられています。
また、verifyingでパスワードの一致もチェックしています。
submitEntry関数では入力されたフォーム値をリクエストから直接bindFromRequestで取得し、検証しています。
結果、バリデーションエラーがあればBadRequest、エラーがなければOkを返しています。

bootstrap2.0を組み込む

scalaテンプレートの修正に入る前に、twitter bootstrap 2.0を組み込んでおきましょう。
2.0へ対応させるにはちょっとだけ作業が必要です。※ここを参考に実装
まず、ここからversion 2.0.xのzipファイルをダウンロードして解凍します。
解凍したbootstrapディレクトリをそのままpublicディレクトリにコピーします。
その後、参考ページにそってviewsパッケージにbootstrapField.scala.htmlとBootstrapHelper.scalaを作成します。
そしてmain.scala.htmlを以下のように修正しましょう。

@(title: Html, nav: String = "")(content: Html)
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
    <meta charset="utf-8">
    <title>@title</title>
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link rel="stylesheet" media="screen" href="@routes.Assets.at("bootstrap/css/bootstrap.min.css")" >
    <link rel="shortcut icon" type="image/png" href="@routes.Assets.at("images/favicon.png")">
    <script src="@routes.Assets.at("javascripts/jquery-1.7.1.min.js")" type="text/javascript"></script>
    <script src = "@routes.Assets.at("bootstrap/js/bootstrap.min.js")" type = "text/javascript" ></script>

    <style>
        body {
            padding-top: 60px; /* 60px to make the container go all the way to the bottom of the topbar */
        }
    </style>
    <link rel="stylesheet" media="screen" href="@routes.Assets.at("bootstrap/css/bootstrap-responsive.min.css")"
</head>
<body>
<div class="navbar navbar-fixed-top">
    <div class="navbar-inner">
        <div class="container">
            <a class="btn btn-navbar" data-toggle="collapse" data-target=".nav-collapse">
                <span class="icon-bar"></span>
                <span class="icon-bar"></span>
                <span class="icon-bar"></span>
            </a>
            <a class="brand" href="#">Project name</a>

            <div class="nav-collapse">
                <ul class="nav">
                    <li class="active"><a href="#">Home</a></li>
                    <li><a href="#about">About</a></li>
                    <li><a href="#contact">Contact</a></li>
                </ul>
            </div>
        </div>
    </div>
</div>

@content

</body>
</html>

scala.htmlファイルを修正

レイアウトファイルの修正ができたら、次はentry.scala.htmlの修正です。
bootstrapのルールにそって記述しましょう。
BootstrapHelperを使えば、bootstrapの記法で記述したコードがPlayのルールに沿った形で展開されます。

@(userForm: Form[User])
@import helper._
@import views.BootstrapHelper._
@title = {ユーザー登録}

@main(title, nav = "signup") {
<div class="container">
    <h1>ユーザー情報登録</h1></br>
    @helper.form(action = routes.UserController.submitEntry) {
    <fieldset>
        <legend>必要事項を入力してください</legend>
        @inputText(userForm("name"),'_label -> "name",'_help -> "input valid name.",'_error -> userForm.globalError)
        @inputText(userForm("email"), '_label -> "email",'_help -> "input valid email.")
        @inputPassword(userForm("password.main"),'_label -> "password",'_help -> "input password.")
        @inputPassword(userForm("password.confirm"),'_label -> "confirm passwod",'_error -> userForm.error("password"))
    </fieldset>

    <div class="actions">
        <input type="submit" class="btn primary" value="entry">
        <a href="@routes.UserController.submitEntry" class="btn">cancel</a>
    </div>

    }
</div>
}

@helper.formでフォームのサブミット先とフィールドを定義しています。
次は結果画面、views/result.scala.htmlを作成しましょう。
この画面ではコントローラから渡されたフォーム情報を表示するだけです。

@(user: User)
@title = {ユーザー登録結果}
@main(title, nav = "signup") {
<div class="container">
  <h1>入力された情報</h1>
  </br>
  <h3>ユーザー名</h3>  
  <p>
    @user.name
  </p>
  <h3>メールアドレス</h3>
  <p>
    @user.email
  </p>
  <h3>パスワード</h3>
  <p>
    @user.password
  </p>
</div>
}

ここまで記述できたらplayコンソールからアプリケーションを起動し、localhost:9000/user/entryへアクセスしてみてください。

$ play
・・・・・
[myblog] $ run

こんな感じの画面が表示されます。入力にエラーがあれば、その旨を伝えるメッセージが表示されます。
※i18n対応は調査中

まとめ

今回はForm機能を使って簡単なフォームを実装してみました。入力チェックも簡単に実装できるので、有用かとおもいます。
次回はDBアクセス部分の実装をしてみます。

参考サイトなど