Zend Frameworkを使って、MVCフレームワークで開発しているとき、Zend_Filter_Inputを使ってフォームで入力された値のフィルタリングを行う処理を記述した時のメモ。
以下のようなフォームで、入力された値についてチェックをかける場合。
1 2 3 4 5 6 7 |
<p>登録する情報を入力</p> <form method="post" acton="/register/confirm"> ユーザー名:<input type="text" name="id" /> パスワード:<input type="password" name="password1" /> パスワードの確認:<input type="password" name="password2" /> <input type="submit" value="登録する" /> </form> |
1 2 |
ユーザ名は半角英数字、5文字以上10文字以下 パスワードは半角英数字、5文字以上10文字以下 |
普通にZend_Filter_Inputを使ってフィルタリング処理をアクションメソッドに書くと以下のようなになります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
class RegisterController extends Zend_Controller_Action { .... public function confirmAction() { $validate = array( 'id' => array( 'Alnum' array('StringLength', 5, 10), 'presence' => 'required', 'message' => array( 0 => ... // Alnumに対するエラーメッセージ 1 => ... // StringLengthに対するエラーメッセージ ) ), 'password1' => array( 'Alnum' array('StringLength', 5, 10), 'presence' => 'required', 'message' => array( 0 => ... // Alnumに対するエラーメッセージ 1 => ... // StringLengthに対するエラーメッセージ ) ), 'password2' => array( 'Alnum' array('StringLength', 5, 10), 'presence' => 'required', 'message' => array( 0 => ... // Alnumに対するエラーメッセージ 1 => ... // StringLengthに対するエラーメッセージ ) ), 'password' => array( 'StringEquals', // 独自のバリデータ 'fields' => array('password1', 'password2'), 'messages' => ... // エラーメッセージ ) ); $data = $_POST; $filter = new Zend_Filter_Input(null, $validate, $data); if(!$filter->isValid()) { // エラー処理 } // DBなどに登録 } ) |
このように書くとアクションメソッドが大きくなってしまってちょっといやな感じだったのでやり方を変えてみました。
以前参加した設計勉強会でのtwkさんの発表や、Ethnaのフィルター処理(もとはStruts?)を参考にしています。
まず、各コントローラーで共通の処理を基底のコントローラにまとめます。
そして各アクションメソッドはサブクラスで実装することにします。
基底のコントローラがBaseControllerで、アクションメソッドを記述するサブクラスをRegisterControllerとします。
1 2 |
BaseController RegisterController extends BaseController |
BaseControllerではフィルターの初期化と入力されたデータのセットを、それぞれinit()とpreDispatch()関数で行います。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 |
require_oncd 'Zend/Controller/Action.php'; class BaseController extends Zend_Controller_Action { ... /** * フォームから入力されたデータ * * @var array */ protected $_form = null; /** * フィルタークラス(Zend_Filter_Inputを継承したクラス) * * @var Wads_Filter_Input */ protected $_input_filter = null; /** * 適用するフィルターのルール(サブクラスで記述する) * * @var array */ protected $_filter_rules = array(); /** * 適用するバリデータのルール(サブクラスで記述する) * * @var array */ protected $_validator_rules = array(); ... public function init() { // コントローラの初期化処理を書く ... // フィルターの初期化 $action = $this->getRequest()->getActionName(); if(!empty($this->_filter_rules[$action]) || !empty($this->_validator_rules[$action])) { $this->_input_filter = new Wads_Filter_Input( $this->_filter_rules[$action], $this->_validator_rules[$action] ); } public function preDispatch() { // アクションメソッドを実行する前の前処理を書く ... // フィルターにデータをセット if($this->_input_filter !== null) { $this->_form = $_POST; $this->_input_filter->setData($this->_form); } ... } } |
各サブクラスではフィルタリングするルールを、$_filter_rules、$_validator_rulesのプロパティーに記述して、各アクションでフィルタリングの処理を実行します。
もしエラーがあった場合は、エラー処理を行います。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
require_oncd 'BaseController.php'; class RegisterController extends BaseController { ... protected $_validator_rules = array( 'confirm' => array( // confirmActionの設定 'id' => array( ... // 上の例と同じ ), 'password1' => array( ... ), 'password2' => array( ... ), 'password' => array( ... ) ) // confirmActionに対する設定の終わり ); ... public function confirmAction() { ... // フィルタリングの処理 if(!$this->_input_filter->isValid()) { // エラーがあった $messages = $this->_input_filter->getMessages(); ... // エラー処理 } // 成功したときの処理 } ... } |
フィルタークラスはZend_Filter_Inputを継承しています。
クラス内でいつも設定するオプションや、エラーメッセージ取得の簡略化など、独自の処理を記述しています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
require_oncd 'Zend/Filter/Input.php'; class Wads_Filter_Input extends Zend_Filter_Input { private $_options = array( 'breakChainOnFailure' => true, 'inputNamespace' => 'Wads_Validator' ); public function __construct($filterRules, $validatorRules, array $data = null, array $options = null) { if($options !== null) { $this->_options = array_merge($this->_options, $options); } parent::__construct($filterRules, $validatorRules, $data, $this->_options); } /** * エラーメッセージを取得する処理 */ public function getErrorMessage($messages = null) { if($messages === null) { $messages = $this->getMessages(); } $err_msg = array(); foreach($messages as $name=>$msg) { unset($this->_form[$name]); $err_msg[] = current($msg); } return $err_msg; } } |
こんな感じで作ってみましたが、ほかの人はどのようになっているのでしょうか。
2008.12.18追記
doitさんにご指摘いただいた部分を修正しました。
基底クラスのメンバをprotectedで宣言されて
派生クラスをprivateで再定義されていますがError
にならないでしょうか?_?
doitさんご指摘ありがとうございます。
私の記述ミスでした。
たしかに、派生クラスのメンバのアクセスレベルは基底クラスのものと同じか、それよりゆるくしないとエラーが出ますね。
ご迷惑おかけしました。