nearproの日記

特に意味はありません。主に読んだ本をただただ記録します。

CakePHP2でリダイレクト先にバリデーションメッセージを表示する


Tutoriel CakePHP : Validate & Redirect - YouTube

登録画面やログイン画面なんかは、たいていアクションとビューが一致しているので特に気にしなくてもバリデーションエラーの時に、Cake側がメッセージ等々を表示してくれます。

ところが、たとえば記事に対するコメントや、一覧画面に追加フォームを設置したいこともあります。その場合、たいていはバリデーションエラーでsaveができないと、元の画面にリダイレクトしたかったりします。そうするとsetFlashしたメッセージは表示できてもバリデーションエラーはリダイレクト時に破棄されるためフォームが書いてあるビューには表示されません。

上の動画では、そういった場合の対応方法が解説されています。

具体的には、バリデーションのエラーがあった場合にセッションに書き込んでそれをコントローラ側で読み取ってコントローラの持ってるModel::validationErrorsに渡せば良いだけだった。

例えば、ユーザのトップ画面で(別のモデルの)ブックマークを保存したいとします。 ブックマークのURLを下のようにバリデートするとします。

<?php
class Bookmark extends AppModel{
  public $validate = [
    'url' => [
        'rule' => ['url',true],
        'message' => 'Invalid url'
    ]
  ];
}
?>

まず、実際に保存するモデルに紐付いたコントローラで失敗時にバリデーションエラーをセッションに書き込みます。

<?php
class BookmarksController extends AppController{
  public function add(){
    if( $this->Bookmark->save($this->request->data) ){
      $this->Session->setFlash(__('The bookmark has been saved.'));
    } else {
      //バリデーションやSQLエラーで保存できない場合
      $this->Session->write('errors.Comment',$this->Bookmark->validationErrors);
      $this->Session->setFlash(__('Failed to save.'));
    }
  }
  $this->redirect( $this->referer() );   // 今回のキモ。自前でrenderしないでどこかにリダイレクトする
}
?>

次に、ビューに紐付いたコントローラ側でセッション情報を読み取らせます。AppModel::beforeFilter()に書いてしまったほうが楽です。(ビデオではそうしています)

<?php
class UsersController extends AppController{
  var $components = ['Session'];

  public function beforeFilter(){
      if($this->Session->read('errors')){
        foreach($this->Session->read('errors') as $model => $errors){
          $this->loadModel($model);
          $this->$model->validationErrors = $errors;
        }
        $this->Session->delete('errors');  //表示は遷移・リロードまでの1回っきりなので消す
      }
  }

}
?>

こうすることで、リダイレクト先のフォームにもバリデーションの結果が表示されるようになります。 (FormHelper以外でフォームを出している場合は、バリデーションエラーをちゃんと表示するようなロジックも必要になりますが)

そういえば、読書記録以外にはじめて書きました。