[Grails]コマンドオブジェクトとバリデーション（ついでにエラーメッセージ with i18N）

# 

[コマンドオブジェクトの公式はここ](http://grails.jp/doc/latest/guide/theWebLayer.html#commandObjects)
[バリデーションの公式はここ](http://grails.jp/doc/latest/guide/single.html#validation)
[制約の一覧はここ](http://grails.jp/doc/latest/ref/Constraints/Usage.html)
[SpringのErrorsはここ](http://docs.spring.io/spring/docs/1.2.x/api/org/springframework/validation/Errors.html)
[SpringのFieldErrorはここ](http://docs.spring.io/spring/docs/1.2.x/api/org/springframework/validation/FieldError.html)

# コマンドオブジェクト

```groovy:HelloController
package bindingtest

class HelloController {

    def index() {
        // コマンドオブジェクトを生成する場合、ドメインと違ってコンストラクタにparamsを渡すことができない。
        // 一旦インスタンスを生成してからプロパティに値を代入することはできる。
        // こうしておけば、view側で最初に表示するデフォルト値を格納することができる。
        // ドメインの場合は問題ないけど、コマンドオブジェクトを使う場合はこうしたほうがいいかもね。
        def cmd = new TestCommand()
        params.name = "default-value"
        params.age = -1
        cmd.properties = params
        render view :"index", model:[testCommand:cmd]
    }

    def test1(TestCommand cmd) {
        if(cmd.hasErrors()) {
            render view:"index", model:[testCommand:cmd]
//            ちなみにコントローラでは以下のようにエラーメッセージを取得出来る。
//            def result = ""
//            cmd.errors.fieldErrors.each { FieldError fieldError ->
//                result += message(error: fieldError) + ":${fieldError.field}(${cmd.errors.getFieldErrorCount(fieldError.field)})    <br />"
//            }
//            render result + "${cmd.errors.getFieldErrorCount()}"
        } else {
            render "${cmd.name}, ${cmd.age}"
        }
    }
}

class TestCommand {
    String name
    Integer age
    String email

    static constraints = {
        name blank: false, minSize: 2
        age min: 1
        email email:true, nullable: true
    }
}
```

# ビュー（GSP）でエラー表示
BEAN.errors.getFiled("FIELD_NAME")の戻り値はSpringのFieldErrorクラスのインスタンス。
FieldErrorクラスのインスタンスを`g:message` タグの　`error`に渡せば、該当するメッセージをi18nから検索して該当メッセージを表示してくれる。該当するものがなければデフォルトメッセージが表示される。


```jsp:index.gsp
<%@ page contentType="text/html;charset=UTF-8" %>
<html>
<head>
    <title></title>
    <style type="text/css">
    .errors {
        color: #FF0000;
    }
    </style>
</head>
<body>
<p style="font-weight: bold">エラーメッセージを一度に表示</p>
<g:renderErrors bean="${testCommand}" />

<p style="font-weight: bold">エラーメッセージを一度に表示（各エラーメッセージを取得しつつ表示しているので、CSSの指定とか細かく制御出来る。）</p>
<g:hasErrors bean="${testCommand}">
    <ul>
        <g:eachError var="err" bean="${testCommand}">
            <li><g:message error="${err}" /></li>
        </g:eachError>
    </ul>
</g:hasErrors>

<p style="font-weight: bold">詳細（普通は使わないと思う）</p>
<g:hasErrors bean="${testCommand}">
    <ul>
        <g:eachError var="err" bean="${testCommand}">
            <li>${err}</li>
        </g:eachError>
    </ul>
</g:hasErrors>
<g:form controller="hello" action="test1">
    <div class='${hasErrors(bean:testCommand, field:"name", "errors")}'>
        Name: <g:textField name="name" value="${fieldValue(bean:testCommand, field:"name")}" />
        <g:hasErrors bean="${testCommand}" field="name">
            <g:message error="${testCommand.errors.getFieldError("name")}" />
        </g:hasErrors>
    </div>

    <div class='${hasErrors(bean:testCommand, field: "age","errors")}'>
        Age: <g:textField name="age" value="${fieldValue(bean:testCommand, field:"age")}"/>
        <g:hasErrors bean="${testCommand}" field="age">
            <g:message error="${testCommand.errors.getFieldError("age")}" />
        </g:hasErrors>
    </div>

    <div class='${hasErrors(bean:testCommand, field: "email","errors")}'>
        Email: <g:textField name="email" value="${fieldValue(bean:testCommand, field:"email")}"/>
        <g:hasErrors bean="${testCommand}" field="email">
            <g:message error="${testCommand.errors.getFieldError("email")}" />
        </g:hasErrors>
    </div>
    <g:submitButton name="submit" />
</g:form>
</body>
</html>
```

# メッセージのカスタマイズ
i18n/message_xxxxxxx.propertiesにメッセージを追加します。
今回の場合は日本語環境のブラウザからアクセスされることを前提として、messages_ja_propetiesの最後に以下の行を追加してみます。

```:grails-app/i18n/messages_ja.properties
bindingtest.TestCommand.age.min.error=もっと大きい値をお願い！
bindingtest.TestCommand.age.nullable.error=なんか入力しろよ！
bindingtest.TestCommand.age.typeMismatch.error=数字を入力しろよ！
bindingtest.TestCommand.email.email.error=メールアドレスを入力しろよ！
```

パッケージ名からフルで指定してあげることで他のメッセージとの衝突を防ぐことができます。
上記の **min** の部分が制約名になります。
利用できる制約名の一覧は[制約の一覧はここ](http://grails.jp/doc/latest/ref/Constraints/Usage.html)から確認できます。
なお、上記ページに記載はされていませんが、 **typeMismatch** という制約（といっていいのか？）が利用できます。
今回のように数字を要求しているのに、数字以外が入力されて場合に利用されます。
また、右メニューに各制約の詳細ページへのリンクがあります。
そこでErrorCodeも記述されています。が、各プロパティの終わり（一番右側）は必要ないみたいです。