Zend_Input_Filter をどこに書くのか

via wadsのblog さん

このように書くとアクションメソッドが大きくなってしまってちょっといやな感じだったのでやり方を変えてみました。

http://wadslab.net/2008/12/zend_filter/

バリデーションをどこに書くかは悩んだ。
Phwittr では wads さんが最初に書かれているように、Action にベタベタと書いていた。
でもこれってやっぱ見通し悪くなるし、何よりテストがやりにくい。
その反省もあって、自分用に作っているのでは完全に別ファイルにしている。


コントローラはこんな感じ。

<?php
class IndexController extends Gene_Controller_Action_Abstract
{
    public function preDispatch()
    {
    }
    public function confirmAction()
    {
        $validator = Gene::getService('Index_Services_Validators_Hoge');
        if ($validator->isValid($this->_getAllParams()) === false) {
            // エラー処理
            return;
        }
        // DB に登録
    }
}

getService() という static なメソッドがビジネスロジックを呼び出すためのメソッド。引数にクラス名を与えると自動的に生成してくれるイメージ。
バリデーションは以下のように。

<?php
class Gene_Services_Validator
{ 
      protected $_rules = null;
      public function isValid($data)
      {
           if (is_null($this->_rules)) {
                $this->_setDefaultRules();
           }
          $this->_validator = new Zend_Filter_Input(null, $this->_rules);
          $this->_validator->setData($data);
          $ret = $this->_validator->isValid();
          if ($ret === false) {
              // エラーメッセージを取得したりする
          }
          return $ret;
      }
}

バリデーションのルールは以下のように。
ルールごとにファイルが増えたり、$this->_rule にバリデーションルールを代入するメソッドを追加したり。

<?php
class Index_Services_Validators_Hoge extends Gene_Services_Validator
{
      protected function _setDefaultRules()
      {
          $validations= 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に対するエラーメッセージ
                       )
              )
          );
          $this->_rules = $validations;
      }
}

というわけで、id:hiro_y さんが書かれている

ふと思ったこと。validationって業務ロジックだよね。

validationは業務ロジック - hiroyのブログ

には全面的に同意です。


バリデーションを分ける事によって、このクラスだけをユニットテストも出来るし、Zend_Filter_Input に依存しないバリデーションも書ける。
例えば POPO(Plain Old PHP Object) で書いたりとか Piece_Right*1とか他のライブラリを使ったりとか。

*1:Piece_Right も使ってみようとしてみたが、 E_STRICT では警告が沢山でるので、PHP5.3 対応待ち