読者です 読者をやめる 読者になる 読者になる

astamuse Lab

astamuse Labとは、アスタミューゼのエンジニアとデザイナーのブログです。アスタミューゼの事業・サービスを支えている知識と舞台裏の今を発信しています。

Play Framework 2.5で入力チェックしてみる

Scala Play Framework Framework

https://www.playframework.com/assets/images/logos/play_full_color.png

アスタミューゼ 開発部のYanagitaです。

Play Frameworkの記事も3回目になります。
連載的な形で続けているので過去分のリンクを貼っておきます。
Play Frameworkが初めての人や前回の見直しはそちらへ

lab.astamuse.co.jp

lab.astamuse.co.jp

今回は前回予告していた入力チェックについて書きます。

事前準備

今回も前回の延長で説明を行います。
事前準備がまだの方はこちらを参照にして下さい。

それから少しそれますが、Play Frameworkはリリースの周期が早く最新版はv2.5.10(2016/12/08時点)となっています。
なるべく最新のバージョンに合わせて記載したいので、v2.5.10から始める方向けにテンプレートプロジェクトの作成と本記事のための修正を記載しておきます。

※ 今回はGiter8を使用してテンプレートプロジェクトを作成します。

1. 作業環境にsbt(v0.13.13以降)をインストールします。

2. ターミナルで以下のコマンドを実行します。

sbt new playframework/play-scala-seed.g8

この後、プロジェクトの名前(本記事では「sample-app」)、組織名などを聞かれますが、デフォルトのままでも問題ありません。
ここからは本記事のための修正です。

3. Filtersライブラリのコメントアウト
sample-app/sample-app.sbt

libraryDependencies += filters ← 10行目あたり
↓
// libraryDependencies += filters

4. Filtersクラス(sample-app/app/Filters.scala)の削除

削除したのはCSRF(クロスサイトリクエストフォージェリ)対策のフィルターになります。
過去の記事で使用していないため今回無効化しました。

でわでわ、本題の入力チェックに入っていきます。

入力チェック

これまでの記事を試された方は既にお気づきかもしれませんが、 前回説明したマッピングクラスには既に入力チェック機能が含まれています。
今回はこのマッピングクラスの入力チェックをコントロールして、目的に合った入力チェックの実装方法を説明します。
まずは前回作成したフォームに下記の入力チェックをかけてみます。

  • 名前(name) ・・・ 入力必須、かつ、入力文字数を2文字以上
  • 性別(sex) ・・・入力必須のみ
  • 誕生日(birthday) ・・・ 未入力を許容する
  • 身長(height) ・・・ 入力必須、かつ、100以上
  • 血液型(bloodType) ・・・ 入力必須のみ

ソースコードだと以下の通り修正
/sample-app/app/controllers/SampleController.scala

// 入力チェック適用前
val form = Form(
   mapping(
      "name" -> text,
      "sex" -> text,
      "birthday" -> date,
      "height" -> number,
      "bloodType" -> text
   )(RequestForm.apply)(RequestForm.unapply)
  )

// 入力チェック適用後
val form = Form(
   mapping(
      "name" -> nonEmptyText(minLength = 2), // 入力必須、かつ、入力文字数を2文字以上
      "sex" -> nonEmptyText,                               // 入力必須のみ
      "birthday" -> optional(date),                        // 未入力を許容する
      "height" -> number(min = 100),                   // 入力必須、かつ、100以上
      "bloodType" -> nonEmptyText                    //  入力必須のみ
    )(RequestForm.apply)(RequestForm.unapply)

textクラス以外のマッピングはデフォルト入力必須のチェックが入ります。
textクラスは未入力/空文字を許容してしまうため、入力必須にする場合はnonEmptyTextクラスを使用します。(name, sex, bloodTypeの設定を参照)
逆に未入力を許容する場合は、optionalクラスを使って未入力を許容します。(birthdayを参照)
ここでoptionalを使用した場合、マッピング先の変数をOption型に変更が必要となります。

/sample-app/app/forms/RequestForm.scala

// 変更前
case class RequestForm(
                        name: String, // 名前
                        sex: String, // 性別
                        birthday: Date, // 誕生日
                        height: Int, // 身長
                        bloodType: String // 血液型
                      )

// 変更後
case class RequestForm(
                        name: String, // 名前
                        sex: String, // 性別
                        birthday: Option[Date], // 誕生日 ← Date型からOption[Date]型に変更
                        height: Int, // 身長
                        bloodType: String // 血液型
                      )

入力文字数や入力値の条件については各マッピングの引数に宣言します。 例えば、入力文字数を2文字以上とする場合は、引数に「minLength=2」を設定します。(nameを参照)
逆に文字数の上限を設定する場合は、引数に「maxLength = 上限値」を設定します。
入力値の制限についても変数名が変わるだけで設定方法は同じです。

上記の入力チェックであればマッピングの入力チェック機能で実現できます。

ここまで実装できたらエラーメッセージの表示実装に移るケースが多いと思いますが、
今回フォームの表示に使用しているForm template helpersは、エラーメッセージの表示機能も含まれているためテンプレートの修正は不要となります。
が、そのままではエラーメッセージの見分けがつかないので(黒文字表示となるため)input.htmlテンプレートにStyleを追加して、 エラーメッセージを赤色にして目立たせます。

/sample-app/app/views/sample/input.scala.html

    <!-- titleタグの下に追加 -->
    <style>
        dd.error { color: red; }
    </style>

では、早速動作を確認してみましょう。

初期表示
f:id:astamuse:20161209114417p:plain

入力エラー後
f:id:astamuse:20161209114416p:plain

エラーメッセージが確認できましたね。
基本的なチェックであればマッピングクラスの機能で十分ですが、実際開発をしてみるとそれだけでは足りないケースがあります。
例えば、メールアドレスや郵便番号など入力フォーマットが決まっている場合の入力チェックです。 この場合は、マッピングクラスのverifyingメソッドを使用します。 今回は名前の入力チェックに英字のみを許容するよう変更します。

/sample-app/app/controllers/SampleController.scala

// 名前(name)に英字のみチェックの適用前
val form = Form(
   mapping(
      "name" -> nonEmptyText(minLength = 2),
      "sex" -> nonEmptyText,
      "birthday" -> optional(date),
      "height" -> number(min = 100),
      "bloodType" -> nonEmptyText
    )(RequestForm.apply)(RequestForm.unapply)

// 名前(name)に英字のみチェックの適用後
val form = Form(
   mapping(
      "name" -> nonEmptyText(minLength = 2).verifying("Alphabet only", name => "^[a-zA-Z]+$".matches(name)), // ← 入力条件に一致する場合のみTRUEを返す
      "sex" -> nonEmptyText,
      "birthday" -> optional(date),
      "height" -> number(min = 100),
      "bloodType" -> nonEmptyText
    )(RequestForm.apply)(RequestForm.unapply)

では、もう一度動作確認
入力内容
f:id:astamuse:20161209121204p:plain

入力エラー後
f:id:astamuse:20161209121202p:plain

名前に数字が含まれているので入力エラーになりましたね。

という風に、基本的なところはマッピングクラスの入力チェック機能を使用し、特殊なチェックはverifyingメソッドを使用することで多少複雑なチェックもカバーできます。

が、しかし
たまに2つの項目を同時にチェックする相関チェックとよばれる入力チェックも存在します。
これについては次回説明します。 今日はここまで

エンジニア&デザイナーの皆様へ

アスタミューゼではまだまだエンジニア&デザイナーを募集中しています。
すこしでも気になった方は下のバナーから採用情報をご確認下さい!

でわでわ

Copyright © astamuse company, ltd. all rights reserved.