ZendFramework 2.0でバリデータのバリデーションメッセージをカスタマイズするメモ

  1. translatorで翻訳する
  2. translatorのテキストドメインを使う
  3. 個別のメッセージを設定する
  4. Zend\Formで使う

translatorで翻訳する。

デフォルトのバリデーションメッセージを翻訳してみます。
http://framework.zend.com/manual/2.0/en/modules/zend.validator.messages.html#using-pre-translated-validation-messagesv
ドキュメントにあるとおりですが、SkeltonApplicationと同様にtranslatorをセットアップすることで、translatorをServiceManager(ServiceLocator)から取得し、
Zend\Validator\AbstractValidator::setDefaultTranslator($translator);
とセットするコードをどこかに書くことになりそうです。
ZF2になって、Validationサービスでも立っているのかと思いましたが、このあたりは従来通りのようです。

translatorでテキストドメインを使う。

翻訳ファイルが巨大になるとメンテナンスが面倒になりますので、translatorファイルをバリデーションメッセージ用に分割してもよさそう。
translator側でテキストドメイン用の設定をするのはZend\I18nのドキュメントにあるとおりで、バリデーター側にその情報を伝えます。
validatorのオプションで'translatorTextDomain'をキーにテキストドメインを設定します。

個別のメッセージを使う。

基本的には組み込みのバリデーションメッセージを翻訳を利用しますが、お客様向けのメッセージとしてはサービス用の内容を含んでいないので不親切です。そこで、個別のメッセージを設定します。
APIドキュメントを読むと、
$validator->setMessage($message, $key);
という具合に、キーを指定することで発生したエラーの種類毎にメッセージテンプレートを設定できます。翻訳ファイルはこのキー名でもメッセージでも翻訳可能です。
InputFilterを設定から作る際、InputFilterのfactoryはこの点、完全対応しているわけではなく、オプションの'message'キーで設定するとそのバリデーターチェーンのエラーメッセージを完全に置き換えられます。これはケースバイケースで、エラーのバリエーションが1つでよいときはこれで十分でしょう。
種類が多様になる際は、setMessageメソッド経由で設定することになります。
サンプルはベタですが、こういった処理は、DIのインスタンスマネージャーを使うと管理しやすくなると思います。

Zend\Formで使う

Zend\Formの$form->isValid()で使うInputFilterも同様ですが、いくつかの方法があるため利用目的に合わせて検討する必要があります。

  • InputFilterAwareInterfaceなエンティティ(もしくはオブジェクト)をバインドする
  • Elementのデフォルトのバリデーションを有効にする
  • 個別に定義する

下記では、$form->getInputFilter()でinputFilterを取得してinputFilterにaddしています。

Zend\Formとの連携で注意すること

Zend\Formはデフォルトで、各エレメントが持っているInputSpecificationからInputFilterを構築するようになっています。HTML5の要素など利便性が高いデフォルトのフィルターとバリデーションがセットされているので、これは通常は使いたい機能です。このデフォルトのInputFilter設定は、
$form->getInputFilter()がコールされたときに、その要素のためのInputFilterにマージされます。
フォームのバリデーションではgetInputFilterがコールされるので、デフォルトにバリデーションが先頭に入ってくることになります。
この動作を停止するには、フォームクラスを書く際に、$useInputFilterDefaults = false;とすればよいのですが、これは習慣的にやらないほうがいいかなと。
さて、Zend\InputFilter\Inputが個別のエレメント用のInputFilterになりますが、この動作として、required=>true時は、isValid()コール時にバリデーションチェーンの先頭がNotEmptyバリデーターでなければ、設定なしNotEmptyを'break_chain_on_failure' => trueで追加し、required=>falseの際には値が空の場合は、validとしてバリデーションを終了します。結果的にオプション設定したNotEmptyバリデータを設定時に追加しても意味をなしません。(何を言ってるかわからないと思いますが・・・!)NotEmptyバリデータのメッセージを変更したいときは、下記のパッチを充ててInputFilterにNotEmptyバリデーターを設定するとできます。上のサンプルはこれが前提になっています。
https://github.com/noopable/zf2/commit/4e0bec36a95a334c71a564b2496eedbebc8b0c2a
※追記 あるバグが解決されて
https://github.com/zendframework/zf2/commit/4fd2d42e58245f03d1909306a8ef8858974611b1
手順として、

  1. フォームにエレメントを追加する
  2. getInputFilter()で初期化する
  3. 変更したいエレメントの先頭に設定変更したNotEmptyバリデータを入れる

これで大丈夫そうです。

素直にElementを継承して書く

上記した問題は、プリセットなエレメントをそのまま使い、設定だけで処理をしようとしたときにNotEmptyバリデータのカスタムメッセージを設定したいという限られたときにだけ発生するわけですが、ライブラリに手を入れなくても、個別のエレメントクラスを継承して書き、getInputSpecification()で、requiredにしたいエレメントについては、NotEmptyバリデータを先頭に書いておくという方法が素直かもしれません。
http://framework.zend.com/manual/2.0/en/modules/zend.form.elements.html