Zend_Frameworkにおけるバリデーションコードの置き場所

Zend_Frameworkにおけるバリデーションコードの置き場所ってどうよ? ってのをいろいろ試行錯誤した結果、今のところこうしてるよ、というのをまとめてみる。

DBアクセスレイヤーは基本的にZend_Db_Tableを使っている。テーブルにマッピングできないものも、データ操作レイヤーに関するものはapplication/models/以下に置いて、Zend_Db_Tableと同じようなインターフェースを持たせて使う。で、共通化できるバリデータはこのモデルクラスにまとめて持たせる。

class ModelBase extends Zend_Db_Table
{
  protected $_validators = array();
  public function getValidators()
  {
    return $this->_validators;
  }
  public function getValiator($name)
  {
    if (!isset($this->_validators[$name]) {
      $this->_validator[$name] = new Zend_Validate();
    }
    return $this->_validator[$name];
  }
}

みたいなベースクラスを作っておいて、 各実装クラスでは、

class FooTable extends ModelBase
{
  public funciton init()
  {
    $this->getValidtor('foo')
    ->addValidtor(new Zend_Validate_StringLength(5, 15));
  }
}

みたいな感じでinit()内でバリデータをセットしておく。そうすると、Zend_Db_Table_Rowとかでこのバリデータを使いたい場合は、

class FooTable_Row extends Zend_Db_Table_Row
{
  public function isValid()
  {
    $result = true;
    foreach ($this->getTable()->getValidators() as $name => $validator) {
      $result = $validator->isValid($this->$name) && $result;
    }
    return $result;
  }
}

なんて感じでバリデーションを書ける(上の例は共通バリデータをぶん回すだけでOKな場合の話ね)。また、このFooTableに対してのCRUDを行うようなZend_Formでは、

class FormFoo extends Zend_Form
{
  public function init()
  {
    $fooTable= new FooTable();
    $foo = $this->createElement('input', 'foo');
    $foo
    ->addValidator($fooTable->getValidator('foo'))
    ->addValidator(new CustomValidatorForForm());
    $this->addElement($foo);
  } 
}

なんて感じでFooTableの該当パラメータ用のバリデータを使うことができるし、それにフォーム専用の追加パラメータを追加することもできる。

アクションコントローラのパラメータの場合は、

class FooController extends Zend_Controller_Action
{
  public function fooAction()
  {
    $foo = $this->_getParam('foo');
    $fooTable = new FooTable();
    $validator = $fooTable->getValidator('foo');
    if (!$validator->isValid($foo)) {
      // IIIINNNNVALIIIIIIIDDDDDDDDDDD!!!!!!!!!!!!!!!!
    }
  }
}

なんて感じで、使いたい要素のバリデータだけ引っ張り出して使える。

ということで、バリデータはモデルクラスにパラメータ名ごとに共通(使い回せる)部分を突っ込んでおいて、シチュエーションごとにそれを取り出しつつ、シチュエーションに特化したバリデータはその場でaddValidator()して使う、いう置き方が一番きれいなんじゃないかと思うのだが、どうだろう。

フィルターに関しては、まとめて同じ処理をしたいという要求がそれほど大きくない、というか同じパラメータでも入力シチュエーションによってどういうフィルターをかけたいかが変わるんで、必要な場所(アクションメソッドとかフォーム要素とか)にそれぞれ書くのが無難な気がする。フィルタークラス自体はアプリケーションに特化したものをapplication/filters以下に用意しておいた方が楽だけど。

と、久しぶりにZend_Frameworkで新しいアプリを書こうと思い、どうせならZend_Frameworkのコンポーネントを使い倒した書き方にしてみるかといろいろ試行錯誤した結果、こんな感じになったという話でした。

Windows Server 2008でManaged DirectXを使ったASP.NET MVC3が動かないという、ググってもレアケース過ぎて解決策が見つからないシチュエーションからの現実逃避の一環でもある。単にDirect 3Dのベクトル演算関数をサーバーサイドでも使いたいだけなんだけどなー。なんで依存関係が解決できないんだろう。

関連する投稿: