play!のplay.data.Formには様々な便利な使い方があります。Springを利用した変換処理、そ
の過程でのValidation(これもSpring)など。Formへの変換処理は独自に規定することもで
きます。Formが取り扱うClassのプロパティにアノテーションをつけることで、バリデーション
ルールも適用できます。アノテーション自体の拡張もできます。
Formクラスの中身を知らなくても十分安全安心に使える優れものです。今回は、少しFormの
中身も覗きながら行ってみます。
目次
フォームに変換する方法
フォーム用のヘルパーがいくつかある。
ClassからForm<T>に変換する
public class User {
public String email;
public String password;
}
public class Application extends Controller {
public static Result index() {
Form
return ok(“sample”);
}
}[/java]
MapからForm<T>に変換する
public class Application extends Controller {
public static Result index() {
Map
anyData.put(“email”, “soylatte@example.com”);
anyData.put(“password”, “secret”);
User user = userForm.bind(anyData).get();// ←
return ok(“sample”);
}
}[/java]
requestからForm<T>に変換する
対象となるModel(ここでいうUser)の各種プロパティがgetterとsetterを持っていないと、変換されませんでした。
public class Application extends Controller {
public static Result index() {
User user = userForm.bindFromRequest().get();// ←
return ok(“sample”);
}
}[/java]
springが利用されている
これらの変換処理にはspringのDataBinderが利用されています。
ソースは長すぎるので、かいつまんでbindFromRequestを見てみます。
//play.data.Form
public Form
return bind(requestData(), allowedFields);
}[/java]
・requestDataメソッド
ここで使われているrequestData()は、リクエストボディの中身を、urlFormEncoded、
multipartFormData、jsonData、クエリごとに一旦Map化して、戻り値であるMap<String,String>
に統合して返してくれる処理です。
・bindメソッド
そのMapをbind(Map<String,String> data, String… allowedFields)に渡しています。bindにて、
SpringのDataBinderを利用してMap化されたリクエストのBody(=data)を、バリデート&
Form<T>型に変換してくれます。
さらにFormのgetメソッドを呼ぶことで、Form<T>のT型インスタンスが取得できるという流れ
です。
バリデーション
JSR-303アノテーションを使って制約条件を設けることができます。
play.data.validation.Constraintsには、下記が用意されています。
- Required
- Min
- Max
- MinLength
- MaxLength
- Pattern
アノテーションを増やしたい
拡張して電話番号用のアノテーションを作ってみます。
郵便番号 | \d{3}-\d{4} |
携帯番号 | 090-\d{4}-\d{4} |
電話番号 | \d{1,4}?-\d{1,4}?-\d{1,4} |
生年月日 | \d{4}-\d{2}-\d{2} |
メルアド | [!#-9A-~]+@[a-z0-9-_]+\.+[a-z0-9-_]+\.+[a-z0-9-] |
ちょいめもさん。
public class MyConstraints extends Constraints
{
@Target({FIELD})
@Retention(RUNTIME)
@Constraint(validatedBy = PatternValidator.class)
@play.data.Form.Display
(name=”constraint.tel”, attributes={})
public static @interface Tel {
String message() default “電話じゃなきゃダメだよ”;
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
// 電話番号の正規表現
String value() default “\d{1,4}?-\d{1,4}?-\d{1,4}”;
}
}[/java]
独自のバリデーションメソッド
アノテーションじゃなくても、バリデーションメソッドを追加できる模様。
@Required
public String email;
public String password;
// これがバリデーションメソッド。なんら特別な話ではなくて、単に
// メソッドを自分で実装するだけ。
public String validate() {
if(authenticate(email,password) == null) {
return “Invalid email or password”;
}
return null;
}
// 公式ドキュメントにあるauthenticateは用意されているわけでは
// なく、こんな感じでどこかに定義してあるメソッドを呼んでるだけ
private String authenticate(String e, String p){
// 認証処理
}
}[/java]
失敗時の処理
badRequestメソッドが用意されています。
// このformという変数はform.scala.htmlのこと
return badRequest(form.render(userForm));
} else {
User user = userForm.get();
return ok(“Got user ” + user);
}[/java]
まだまだある!Formの便利機能
フォームに一連の情報をセットしたい
たとえばデフォルトの値を指定したいときにplay.data.Formのfillメソッドが利用できます。
get()以外でFormの値にアクセスしたい
get()を使えばForm<T>のT型インスタンスを取得できます。インスタンスごと取得するまでもな
いときなど、fieldメソッドが使えます。
これはget()を使った以下の処理と同等です。
User user = form.get();
String name = user.getName();[/java]
独自のデータバインディングを実装したい
play.data.format.Formattersクラスのregisterメソッドにて、独自のデータバインディングルール
を登録します。内部的にはorg.springframework.format.Formatter<T>を利用しています。
// データバインディングの対象となるclassを第一引数に渡します。
public static
(final Class
[/java]
第一引数のクラスを変換するときは独自のルールを使ってね、という約束が生まれます。
実際の用例です。
//LocalTimeの変換は独自のルールを使ってね、という約束を登録します
Formatters.register
(LocalTime.class, new Formatters.SimpleFormatter
{
// 変換するルールを正規表現で記載しています。
private Pattern timePattern
= Pattern.compile(“([012]?\\\\d)
(?:[\\\\s:\\\\._\\\\-]+([0-5]\\\\d))?”);
/** 入力時の変換ルール */
@Override
public LocalTime parse(String input, Locale l)
throws ParseException {
// 入力されたStringが正規表現と合致しているかどうか
Matcher m = timePattern.matcher(input);
if (!m.find()) throw new ParseException(“No valid Input”,0);
int hour = Integer.valueOf(m.group(1));
int min = m.group(2) == null
? 0
: Integer.valueOf(m.group(2));
// 無事変換された値を返す
return new LocalTime(hour, min);
}
/** 出力時の変換ルール */
@Override
public String print(LocalTime localTime, Locale l) {
return localTime.toString(“HH:mm”);
}
});[/java]
アセスメント
- Form変換処理の内部ロジックの概略を理解している
- Formのバリデーションを指定する方法が分かる
- Formのバリデーションを拡張する方法が分かる
- Formにデフォルトの値を設定する方法が分かる
公式ドキュメントのForm definitionsを参考にしました。