Symfonyではアクションの処理の前にフレームワークによるパラメータの検証(validate)をサポートしています。 本日は、作成中の個人用ブックマークシステムのサンプルに検証の仕組みを実装してみます。
ブックマークの更新用アクションはbookmark/editとbookmark/addですがどちらもサブミットを押したときにbookmark/updateアクションにポストするようになっています。検証はこのポスト先のアクションでサーバサイドの検証を行います。今回はクライアントサイドの検証は行いません。
検証の流れ
検証の流れは大まかに下の様になっています。 [comment: アクション名.ymlを「検証用設定ファイル」と呼ぶことにします。] 1. アクションの実行(execute)前にapp/modules/module/config/アクション名.ymlの存在を調べあれば検証を始める。 2. 検証結果が成功ならexecuteへ、失敗ならアクションのhandlErrorへ遷移する。
検証用設定ファイル
bookmark/updateアクション用の検証設定は以下のファイルに書くように決まっています。 frontend/modules/bookmark/validate/update.yml
検証用設定ファイルは大きく分けて3つのパートがあります。
一番最初に書くmethodsには検証するHTTPメソッド(post or get)とパラメータを列挙します。注意すべき点として書かなければ検証されずスルーされます。不正値のチェックを目的に検証をを行うのであれば下記のようにgetとpostは併記すべきです。
methods: post: [url, title, comment] get: [url, title, comment]
二番目に、methodsに書いた各パラメータの検証方法を書きます。
names:
url:
required: Yes
validators: urlValidator, dupValidator
title:
required: Yes
validators: titleValidator
comment:
required: Norequiredは、必須かどうかを設定します。必須時のメッセージを設定したい場合はrequired_msgを設定します。設定しない場合には”Required”がデフォルトで設定されます。
validatorsには、次の三番目のパートに記述する検証内容を記述します。
最後に三番目のパートで検証内容を記述します。ここではsfStringValidatorやsfRegexpValidatorのような組み込みの検証クラスを利用することもできますし、myUrlValidatorの様に自分で定義することもできます。
urlValidator:
class: sfRegexValidator
param:
pattern: "~https?://(([a-zA-Z0-9]+\.){1,}[a-zA-Z]+|(\d+.){3}\d+)(:\d+)?(/[-.!\~*\d\w;/?:@&=+$,%#]+)?~"
match_error: "invalid URL pattern"
dupValidator:
class: myUrlValidator
param:
url_error: "URL alread exists"myUrlValidatorは、sfValidatorから派生します。frontend/lib/myUrlValidator.class.phpに記述しました。 自分でValidatorを作るときの注意点があります。検証用設定ファイルのnamesパート内validatorsには、カンマ区切りで複数指定することができます。validatorsに複数のvalidatorを指定した場合には前の検証に失敗しても処理を中断せず以後の検証も行われます。つまり、上の例だとurlの検証でurlValidatorの結果にかかわらずdupValidatorの検証が実行されます。validatorの中で検証以外の処理を行うときには前の検証結果をあてにしてはいけません。前の検証結果により処理を中断したい場合には下記の様にvalidatorのexecuteの先頭で$errorを判別すると良いでしょう。
public function execute(&$value, &$error) { // if error previous validator if(strlen($error)){ return false; }
Spycのバージョンアップ
もし、Symfony rev350以前に組み込まれているSpycを使っていた場合、YAMLの文字列として#が利用できないという致命的な欠陥があります。urlValidatorが うまくいかないときには、バージョンをチェックして最新版と差し替える必要があります。Spycは、Symfonyをインストールしたディレクトリのsymfony/spyc/spyc.phpにインストールされています。
handleError
検証が失敗するとアクション名に対応したhandleErrorをコールします。これはhandleErrorアクション名で定義されるメソッドです。このメソッドがなければアクションクラスのhandleErrorメソッドをコールします。このメソッドは親クラスのsfActionクラスで次の様に定義されています。
public function handleError () { return sfView::ERROR; }
つまり、アクション名Errorビューに遷移します。このままだとupdateError.phpが必要になります。そうではなくて、editSuccess.phpに戻り再入力を促したいと思いますので、下記の様にhandleErrorUpdateを定義します。
public function handleErrorUpdate(){ $bookmark = $this->getBookmarkOrCreate(); $bookmark->setUrl($this->getRequestParameter('url')); $bookmark->setTitle($this->getRequestParameter('title')); $bookmark->setComment($this->getRequestParameter('comment')); $this->setTemplate("edit"); return sfView::SUCCESS; }
これで、bookmark/updateで検証が失敗するとeditSuccessビューが表示されるようになります。
次にeditSuccessビューのテンプレートを書き換え再入力を促すメッセージを表示できるように変更します。
<div> < ?php echo form_error('url') ?> <label for="url">< ?php echo __("URL") ?>:</label> <br /> < ?php echo object_input_tag($bookmark, 'getUrl', array('size'=>50)) ?> </div>
上のように、form_error関数を使ってメッセージを表示するように修正します。
国際化対応
最後にエラーメッセージ用の国際化ファイル(日本語だけですが…)を追加します。
<!-- validate error --> <trans -unit id="100"> <source>Required</source> <target>必須です</target> </trans> <trans -unit id="101"> <source>URL alread exists</source> <target>このURLはすでに存在します</target> </trans> <trans -unit id="102"> <source>invalid URL pattern</source> <target>URLが正しくありません</target> </trans>
肝はなんといっても”Required”の定義ですね。検証用設定ファイルでrequired_msgを一々設定していると大変なのであえて設定せずに対応しています。
最後に
以上で簡単ながら検証処理が追加されました。 結構はまりどころが多かったのでポイントを整理します。
- validate/アクション.ymlには、methodsにpost/getを併記すること
- 自分でValidatorクラスを作ったときには前のvalidatorの結果をあてにするな
- Spycのバージョンが古いと#が使えないので本家のSubversionから差し替える
- messages.ja.xmlで”Required”位は設定したほうがいいかも
説明が下手でと長くなってしまいましたが、Symfonyによるよく整理された検証の仕組みを堪能いただけたら幸いです。

