Transitionコンポーネント - さらに込み入った使い方
というわけでTransitionコンポーネントを便利に使わせてもらっているのですが、このコンポーネントはその性格上以下のような制約があります。
- アクションごとにバリデーションを行う場合はそれごとにモデルが必要になる
画面ごとにきっちりとモデルが割り当てれればいいのですが、1つのモデルのフィールドが複数の画面にちらばるとか、全画面の項目を1つのモデルに割り当てたいとかいろいろな事情があると思います。
実際自分もそうだったので、このTransitionコンポーネントとMultiValidatableというビヘイビアを組み合わせて使ってます。
(Bakery) MultivalidatableBehavior: Using many validation rulesets per model
全ての画面の項目を1つのモデルに押し込んで、画面毎のバリデーションルールを作って、画面毎にしていするっていう形です。
class Wizard extends AppModel { public $name = 'Wizard'; public $useTable = false; public $actsAs = array('Multivalidatable'); public $validate = array(); public $validationSets = array( 'step1' => array( 'field1_1' => array(...), : ), 'step2' => array( 'field2_1' => array(...), : ), 'step3' => array( 'field3_1' => array(...), : ), ); public function beforeValidate($option) { foreach ($this->validationSets as $key => $rules) { $this->validate[] = $rules; } } }
このようにしておけばコントローラでは以下のように指定できます。
class FooController extends AppController { public $uses = array('Foo', 'Wizard'); public $components = array( 'Transition' => array( 'models' => 'Wizard', ), ); public function step1() { // Wizardモデルのstep1のルールでバリデーションして問題がなければ step2 へリダイレクト $this->Wizard->setValidation('step1'); $this->Transition->checkData('step2'); } public function step2() { // step1 から遷移したことをチェックし、 // Wizardモデルのstep2のルールでバリデーションして問題がなければ step3 へリダイレクト $this->Wizard->setValidation('step2'); $this->Transition->automate('step3', 'Wizard', 'step1'); } public function step3() { // step2 から遷移したことをチェックし、 // Wizardモデルのstep3のルールでバリデーションして問題がなければ confirm へリダイレクト $this->Wizard->setValidation('step3'); this->Transition->automate('confirm', 'Wizard', 'step2'); } public function confirm() { // ポストで何らかのデータを受け取ったらセッション情報を展開 if (!empty($this->data)) { $this->data = $this->Transaction->mergedData(); } // step3 から遷移したことをチェックし、 // Wizardの「全部のルールで」バリデーションして問題がなければ、add へリダイレクト this->Transition->automate('add', 'Wizard', 'step3'); // 各画面で入力されたデータをマージする $this->set('post', $this->data); } public function add() { // step1/step2/step3 から遷移したことをチェック $this->Transition->checkPrev(array('step1', 'step2', 'step3', 'confirm')); $data = $this->Transition->mergedData(); if ($this->Foo->saveAll($data)) { $this->Transition->clearData(); } else { $this->redirect(array('action' => 'confirm')); } } }
これで開発中に項目がいろんな画面に移動してもWizardモデルのバリデーションをいじればいいだけになるので、手間はあまりかかりません。また念のためにconfirmアクションでも全項目のバリデーションを書けるようにしたのでちょっと安心感が増します。(まぁ本当に念のためなんですけどね)
そして実テーブル側でもさらに念を入れてバリデーションをしたいとかある場合はWizardモデルを継承して必要なバリデーションルールを使うとかすれば、あっちこっちを修正することにもならないかと思います。