Tech Sketch Bucket of Technical Chips by TIS Inc.

Scala/Play2でWebアプリケーション開発~(3)入力チェックの実装(Javaライブラリの活用とBootstrap3への対応)

Pocket

前回の Scala/Play2でWebアプリケーション開発~(2)Slick2.0で簡単アプリ開発 ではPlay2とSlickを使って、登録・参照・更新・削除を行う簡単なWebアプリケーションを作成しました。しかし、最小限の機能に絞って紹介したため、実際システムとして運用していくには、まだまだ機能が不足しています。それらの機能の実装方法をPlay2でアプリケーション開発を行う際のTipsとして、少しずつ紹介していきます。

今回はその第一弾として、アプリケーションには欠かせないチェック処理をPlay2ではどのように実装するのかを紹介します。単純な単項目チェックから、Javaの資産を活用したチェックや相関項目チェックに加え、チェック結果をBootstrap3に対応して表示するにはどうすればよいかをまとめて紹介します。


単項目チェック1: フォームの型指定によるチェック

前回作成したイベント登録画面は「イベントID」「イベント名」ともに、どんな項目でも入力することが出来ますし、何も入力せずに登録することも出来てしまいます。まずは、この2項目に必須入力チェックを実装します。

チェックの実装

必須入力チェックはフォームのマッピングする型指定により実行できます。入力項目が「text」の場合は、「nonEmptyText」とするだけです。

・[Controller] EventCreate.scala (/app/controllers/event)

「eventId」「eventNm」を「nonEmptyText」にマッピングさせました。入力チェックを行う場合は、コントローラにエラー発生時の処理を実装する必要があります。上記のように「eventForm.bindFromRequest.fold」の第一引数に「hasErrors = {}」としてエラー発生時の処理、第二引数に「success = {}」として成功時の処理を実装します。エラー発生時は入力された内容が画面に維持できるように「form」をそのまま返し、成功時は「Events.create()で」登録処理を行った後、「index」にリダイレクトします。

画面上に入力項目の制約とエラー発生時のメッセージを出力するために「messege」ファイルへの定義が必要です。

・messages (/conf)

必須入力チェックの制約名称を「constraint.required」、エラーメッセージを「error.required」として定義しました。

それでは、イベント登録画面を確認してみましょう。

eventCreate-required1.png

制約「必須」が表示されました。何も入力せず、このまま「登録」ボタンを押してみます。

eventCreate-required2.png

エラーメッセージが表示されました。しかし、前回適用したBootstrapのデザインが完全には適用されていませんので、適用させてみましょう。

Bootstrap3.Xへの対応

「イベントID」や「イベント名」は、「@helper.inputText」を使用してテキストボックスと制約、エラーメッセージを表示しています。これは、Play2の「twitterBootstrapFieldConstructor.scala.html」というフィールドコンストラクタに定義されていますので、このファイルを確認してみましょう。

・[Play2]twitterBootstrapFieldConstructor.scala.html (/views/helper/twitterBootstrap)

Bootstrapの本家サイトにある Formsの定義例 を確認してみると、執筆時点での最新バージョン3.1.0を利用する際の定義内容とは異なることがわかると思います。例えば、一番外側の「div」の「class」に指定されている「clearfix」は使用されていません。

Play2のフィールドコンストラクタを参考にし、「 Migrating to v3.x 」を確認しながら、Bootstrap3.1.0向けにカスタマイズしていきましょう。カスタマイズしたファイルは「Xenlon」(弊社のフレームワーク名)をプレフィックスとして付与することにします。「/views/xenlon/helper/twitterBootstrap」に「xenlonTwitterBootstrapFieldConstructor.scala.html」を作成します。

・[view]xenlonTwitterBootstrapFieldConstructor.scala.html (/views/xenlon/helper/twitterBootstrap)

「clearfix」は「form-group」に「error」は「has-error」にといったように編集しました。「input type="text"」にもclassを指定する必要がありますので、「@Html(elements.input.body.dropRight(2)+"class='form-control'>")」と設定しました。

そして、定義した「twitterBootstrapFieldConstructor.scala.html」は、「Helpers」オブジェクトから呼び出します。「/controllers」に「XenlonHelpers」を作成します。

・[Controller] XenlonHelpers.scala (/controllers)

「xenlonFields」を定義しました。作成した「XenlonHelpers」を「eventCreate.scala.html」でインポートします。

・[View] eventCreate.scala.html (/app/views)

画面を確認してみましょう。

eventCreate-bootstrap.png

テキストエリアが大きくなり、少しBootstrapらしくなりました。

ラベルと入力項目を1行に表示する

さらに、カスタマイズを加え、ラベルと入力項目が同じ行に表示されるようにしておきましょう。

指定する内容は想定画面サイズにより異なりますが、ラベルは「col-xs-3」、入力項目は「col-xs-9」をclassに指定しました。「eventCreate.scala.html」には「role」と「class」を指定します。

・[View] eventCreate.scala.html (/app/views)

classには「form-horizontal」を指定しました。

それでは、イベント登録画面をもう一度確認してみましょう。

eventCreate-horizontal1.png

ラベルと入力項目が1行に表示されました。ラベルに表示されている「イベントID」と「イベント名」の長さがほぼ同じなので分かりづらいですが、長さが異なっても右端が揃ってきれいに見えます。

eventCreate-horizontal2.png

エラーが発生すると、該当項目が赤くなり、エラメッセージが表示されました。

画面上部にエラーメッセージを表示する

入力項目がたくさんあった場合、エラーが発生していることがすぐに分かるように、画面上部にもエラーメッセージを出力しておきましょう。

・[View] eventCreate.scala.html (/app/views)

エラーが発生した場合には、コントローラから引数として受け取ったFormの「erros」にエラー内容がセットされますので、「eventForm.errors」が設定されていた場合に、「入力内容に誤りがあります。確認して下さい。」というメッセージを出力するようにしました。

単項目チェック2: Play2のチェックライブラリを使用する。

「イベントID」「イベント名」は必須入力となりましたので、次は、「イベント名」の文字列長に制約を設けます。確認しやすいように少し短めですが入力できる最大文字列長を5文字としましょう。

最大文字列長のチェックはチェック関数「verifying」を呼び出します。

・[Controller] EventCreate.scala (/app/controllers)

「eventNm」の「nonEmptyText」から「verifying」を呼び出し「nonEmptyText.verifying(maxLength(5))」としました。必須入力チェックと同様に、制約とエラーメッセージを「messages」に追加します。

・messages (/conf)

制約名は「constraint.maxLength」、エラーメッセージは「error.maxLength」を定義しました。

それでは画面を確認してみましょう。

eventCreate-maxlength1.png

「イベント名」に「最大文字列長:5」と表示されるようになりました。6文字以上入力すると、「5文字以内で入力してください。」というメッセージが表示されます。

eventCreate-maxlength2.png

画面上部にもエラーメッセージが表示されました。

単項目チェック3: Play2のチェックをカスタマイズする。

イベント名にはサロゲートペア文字も入力できるようにしたいとします。Play2が用意している「maxLength」チェックでは文字列長をString#lengthでカウントしているため、サロゲートペア文字は2文字としてカウントしてしまいます。サロゲートペア文字を正しくカウントするためには、String#codePointCount()を使用する必要がありますので、カスタマイズしてみましょう。

制約を定義する「XenlonConstraints」オブジェクトを作成します。

・XenlonConstraints.scala (/xenlon/api/data/validation)

「XenlonConstraints」オブジェクトは「Constraints」トレイトをミックスインし、maxLength関数をオーバーライドします。文字列長はString#codePointCountで算出するようにカスタマイズしました。

コントローラは、Playの「Constraints」オブジェクトではなく、「XenlonConstraints」をインポートするようにします。

[Controller] EventCreate.scala (/app/controllers)

サロゲートペア文字を入力して確認してみましょう。

eventCreate-sarogate.png

カスタマイズ版の文字列長チェックが実行され、正しくカウントされるようになりました。

単項目チェック4: 独自のチェックを作成する

「イベントID」にも文字列長チェックを実装します。「イベントID」は必ず5文字で入力しなければならないとしましょう。固定文字列長チェックはPlayでは提供されていませんので、独自のチェックを実装します。

maxLengthと同様に「XenlonConstraints」に「fixLength」を実装します。

・XenlonConstraints.scala (/xenlon/api/data/validation)

String#codePointCountで指定文字数であることをチェックします。

コントローラからは文字列長「5」を指定し、定義した「fixLength」を呼び出します。

[Controller] EventCreate.scala (/app/controllers)

「messages」ファイルに制約とエラーメッセージを追加します。

・messages (/conf)

では、画面で確認してみましょう。

eventCreate-fixlength.png

固定文字列長チェックが実行されました。

準備:(画面入力項目の追加)

もう少し他のチェックを実装するために、画面項目を追加します。URLを入力する「ホームページ」と日付を入力する「開催日」を各画面に追加しましょう。イベント登録画面とイベント更新画面では「ホームページ」「開催日」の登録・変更が出来ます。イベント検索画面では「開催日」の範囲を指定して検索し、イベント一覧にも開催日を表示するようにしましょう。

テーブルへのカラム追加

まず、EVENTテーブルに項目を追加します。

・[Model] Event.scala (/app/models)

caseクラスの定義とテーブル定義に「eventDate」「homepage」を追加し、検索関数「find」の検索条件に「eventDate」を追加します。データベーススキーマへの反映はイベント登録画面から行えます。

イベント登録機能

イベント登録機能を修正します。イベント登録画面に「eventDate」「homepage」を追加します。

・[View] eventCreate.scala.html (/app/views)

イベント登録画面・イベン更新画面とイベント検索画面では入力項目が異なりますので、フォームをマッピングするcaseクラスの定義も別にしておきます。

・[model] Forms.scala (/models)

コントローラはイベントフォームに「eventDate」「homepage」を追加し、「create」関数で「Event」のインスタンス生成時の引数にも追加します。

[Controller] EventCreate.scala (/app/controllers)

イベント更新機能

イベント更新機能を修正します。イベント更新画面に「eventDate」「homepage」を追加します。Bootstrap3の適用、画面上部へのエラーメッセージ表示等、イベント登録画面と同様の修正も加えておきましょう。

・[View] eventUpdate.scala.html (/app/views)

コントローラにもフォーム定義に「eventDate」「homepage」を追加し、その他イベント登録機能と同様の修正を加えます。

・[Controller] EventUpdate.scala (/app/controllers)

イベント検索機能

イベント検索機能を修正します。イベント検索画面の検索条件に「eventDateFrom」「eventDateTo」を追加し、開催日の範囲を指定して検索できるようにします。イベント一覧には「eventDate」を追加します。イベント登録と同様の修正も加えておきましょう。

・[View] eventSearch.scala.html (/app/views)

コントローラにもフォーム定義に「eventDateFrom」「eventDateTo」を追加し、検索関数「find」の引数にも追加します。フォームをマッピングするcaseクラスは「EventSearchForm」に変更しておきます。

・[Controller] EventSearch.scala (/app/controllers)

「messages」ファイルにも「開催日」「ホームページ」を追加しておきましょう。

・messages (/conf)

以上で、3画面に「開催日」と「ホームページ」を追加することが出来ましたので、イベント登録画面から「DROP TABLE」、「CREATE TABLE」の順にクリックしテーブルを再生成しておきましょう。
※アプリケーションは動くけど、Eclipse上でコンパイルエラーが発生してしまう場合は、一度Eclipse上で「リフレッシュ」を行って下さい。

単項目チェック5: Javaライブラリを活用して独自のチェックを作る。

ホームページはURLを入力しますので、入力値がURLとして適切であるかをチェックします。Play2にはURLのチェックはありませんので、独自のチェックを実装する必要があるのですが、チェックロジック自体は既存のJavaライブラリを活用することにしましょう。今回はApacheの「 Commons Validator 」を利用します。

Javaライブラリの参照

「Commons Validator」を使用するため、「Build.scala」にライブラリのMavenリポジトリ指定を追加します。Slickと同様に、「グループ名」「アーティファクト名」「バージョン」を指定します。

・Build.scala (/project)

ライブラリの定義を追加したので、Mavenリポジトリからjarファイルを取得しEclipseでビルドできるようにパスを設定する必要があります。 最初にEclipseプロジェクトを作成した時 と同様にPlayコマンドを実行します。

「play」でPlayコンソールを起動し、「eclipse with-source=true」でEclipseプロジェクトを再生成します。Eclipseのプロジェクトのディレクトリ(筆者の場合は「C:\pleiades\workspace\techApp」)で実行してください。
再生成した内容をEclipseに認識させるため、Eclipse上でプロジェクトのリフレッシュ(プロジェクトを右クリック→リフレッシュ)を行います。最後に、プロジェクトファイルを再生成したことでDBLaucherの設定が変更されていますので、元に戻しておきましょう。

チェック処理の実装

Appacheの「Commons Validation」を使用できるようになりましたので、チェック処理を「XenlonConstraints」オブジェクトに実装します。

・XenlonConstraint.scala (/xenlon/api/data/validation)

「org.apache.commons.validator.GenericValidator」をimportし「isUrl」を追加しました。「isUrl」のチェックは「GenericValidator.isUrl」を呼び出しているだけです。

コントローラから「isUrl」のチェックを実行するように修正しておきます。

[Controller] EventCreate.scala (/app/controllers)

「messages」ファイルに制約とエラーメッセージを追加します。

・messages (/conf)

それではイベント登録画面を確認してみましょう。

eventCreate-url.png

制約が表示され、URLとして相応しくない文字を入力すると、正しくチェックされていることが分かります。今回はApacheのライブラリを使用しましたが、もちろん独自のJavaライブラリがあればそれを利用することも出来ます。

単項目チェック6: 独自のマッピングを作成する

先ほど作成したURLのチェックをさらに使いやすくするため、独自のマッピングを作成してチェックをさせてみます。「xenlon.api.data」パッケージに「XenlonForms」オブジェクトを作成します。

・XenlonForms.scala (/xenlon/api/data)

「XenlonConstraints.isUrl」を呼び出します。コントローラは「import xenlon.api.data.XenlonForms._」をimportし、「homepage」は「text」ではなく「url」を使用するよう変更します。

[Controller] EventCreate.scala (/app/controllers)

予め画面入力項目のパターン(ドメイン)を整理して対応するマッピングを実装しておけば、上記のようにフォームに対する単項目チェックが非常にシンプルに実装することが出来ます。

相関項目チェック: コントローラのロジック内でチェックする

イベント検索画面の検索条件に「開催日」の範囲指定を追加しましたので、「開催日(From)」と「開催日(To)」の2つの項目の相関チェックを追加してみましょう。「開催日(From)」から「開催日(To)」の範囲を指定して検索出来るようにしたいので、入力内容は必ず「開催日(To)」の方が新しい日付「開催日(From)<=開催日(To)」でなければなりません。
相関チェックはフォームをネストして定義したり、タプルを使用することで、単項目チェックと同じようにチェックを実装することが出来ますが、少しフォームが複雑になるので、コントローラのロジック内に実装することにします。コントローラのロジックに実装すると自由なタイミングでチェックを呼び出すことが出来ます。

単項目チェックが成功した際に実行するので、コントローラの「search」関数の「success = {}」内のロジックに実装します。「eventDateTo」と「eventDateFrom」の比較を行い、条件を満たしていない場合はフォームにエラーメッセージを設定し「BadRequest」(ステータスコード:400)を返します。

・[Controller] EventSearch.scala (/app/controllers)

エラーメッセージの設定は「withError」を使用し、第一引数にチェック対象項目名、第二引数にエラーメッセージを指定します。「開催日(From)」欄にエラーメッセージを表示し、「開催日(To)」もエラー表記(赤字)だけ行うためメッセージは空で追加しました。

日付書式のメッセージをまだ追加していませんでしたので、「messages」ファイルに日付書式とエラーメッセージを追加します。

・messages (/conf)

イベント検索画面から確認してみましょう。

eventCreate-date.png

開催日の範囲指定を反対に入力してしまった場合はエラーになりました。

完了メッセージの表示

チェック処理を実装しエラーメッセージが表示されるようになったので、正常時も完了メッセージを表示させるようにしておきましょう。

各画面にメッセージを表示させるので、最初にメッセージ出力を共通化します。メッセージ表示用のHTMLテンプレートとして、「message.scala.html」を作成します。

・[View] messages.scala.html (/views/xenlon/helper)

正常時のメッセージはフラッシュスコープを利用することにします。Play2にはリクエストを跨ってデータを保持するためにセッションとフラッシュスコープにデータを保存することが出来ます。保存先はCookieです。セッションを利用するとユーザがWebブラウザを開いている間データを維持することが出来ますが、メッセージは次のリクエストまでデータが保持されれば十分ですのでフラッシュスコープを使用します。「implicit」で「Flash」を受け取れるよう実装しておきます。続いて、「messages」をイベント登録画面から呼び出すようにします。

・[View] eventCreate.scala.html (/app/views)

これまで、メッセージ出力を直接実装していたところを「@messages(eventForm.errors)」としました。また、成功時のメッセージを出力するため、こちらにも「implicit」で「Flash」を定義しました。イベント更新画面・イベント検索画面のメッセージ出力も同様に修正しておきましょう。
成功時のメッセージはコントローラからフラッシュスコープに設定します。イベント登録の登録処理が完了した際に、「登録しました」というメッセージが表示されるようにしましょう。

「Redirect().flashing()」として、「success」にメッセージをセットしました。「eventCreate.scala.html」で引数として「implicit flash: Flash」を受け取るようにしたので、当HTMLを使用している箇所はすべて「implicit request =>」を定義する必要があります。「createTable」と「dropTable」に追加しておきましょう。

同様にテーブル作成後に「テーブルを作成しました。」、削除後に「テーブルを削除しました。」、イベント更新画面で更新後には「変更しました。」、イベント検索画面で削除後には「削除しました。」のメッセージが表示されるようにしておきましょう。

最後に画面を確認しておきましょう。

イベント登録画面

eventCreate-message.png

イベント更新画面

eventUpdate-message.png

イベント検索画面

eventSearch-message.png

それぞれ、処理の完了メッセージが表示されるようになりました。

 
チェック処理の実装は以上となります。
今回は画面から入力する項目のチェック処理を様々なパターンで実装しました。チェック処理の実装は今回紹介した方法以外にも、「verifiing」メソッドにチェック関数を渡す方法やフォームをネストして相関チェックを行う方法など、様々なやり方がありますので、実際のアプリケーション開発では、適材適所でチェック処理を実装していく必要があります。
Scalaのチェックライブラリ、オジリナルチェックの実装、Java資産の活用方法を紹介しましたが、これらをうまく使い分けることで、Play2ではシンプルにチェック処理が実装できることが分かって頂けたと思います。「まだScalaのライブラリが少なくのでは?」という懸念も、Java資産を簡単に活用できることで払拭されたのではないでしょうか。
今後もこのようなTipsを紹介していきます。

エンジニア採用中!私たちと一緒に働いてみませんか?