それマグで!

知識はカップより、マグでゆっくり頂きます。 takuya_1stのブログ

習慣に早くから配慮した者は、 おそらく人生の実りも大きい。

symfonyにはValidation機能がついている

2009-02-02追記:この記述は古いですよ。symfony1.2ではDjango流に$form.isvalid()です。

Validation機能がついていて、validat+$Action名() の関数を定義するか、module/APP/validate/$Action名.ymlに条件を書けば、isValidを調べてくれるらしい。


でもadmin-generator を使っているときのvalidationについてはわからない。マニュアルのadminには載ってなかったなぁ。DBの定義(schena.yml)から自動的にValidationしてくれるとすごく助かるのだけけれど。どうしようかな。

admin使わないとかgenerat-crudしないなら、Symfonyの魅力半減なんだよなぁ。他のフレームワークでイイじゃんってなる。

admin-generatorの場合

admin-generatorの場合、ACTIONは次の4つが定義されている。CRUD

list, edit, create, delete

ここから想像するとeditフォームにvalidationを追加したいならば、

module/APP/validate/edit.yml

を作ればO.K. だと予想してみる、。

ファイル作った

ファイル作るだけで読み込んだ成功。でも空ファイルなんで、Validation条件が無くエラー落ち。
やっぱよくわからない。

manualにあるYAMLをコピペしてみた

fields:
  name:
    required:
      msg: "nameは必ず入力してください。"

で、実行してみた。お、なんか行けそう?表示は出来る。送信してみた。やっぱダメか。。。

There are some errors that prevent the form to validate

ってエラー。form を validateするのに邪魔が入りました。エラーの所為です。と。

どうすればイイのだろう。とりあえず、手詰まり。

Manualに戻ってみる

The validation process doesn't stop when a validator fails. Symfony tests all the validators and declares the validation failed if at least one of them fails. And even if some of the rules of the validation file fail, symfony will still look for a validateXXX() method and execute it. So the two validation techniques are complementary. The advantage is that, in a form with multiple failures, all the error messages are shown.

validation processはvalidatorがfailしても止まらず、symfonyは出来るだけ多くのvalidatorと、declareの条件をテストしようとする。条件を記述したファイルでValidationが失敗だったとしても、さらにvalidateXXX()メソッドの実行を試みる。それがsymfonyのvalidation機構。なのでfileに記述する手法とメソッドを作成する手法は両立するし、競合しない。この利点は、不一致条件のエラーメッセージをまとめて一度にユーザーに伝えることが出来る点だ。

Validation files are located in the module validate/ directory, and named by the action they must validate. For example, Listing 10-19 must be stored in a file called validate/send.yml.

Validation ファイルは、modules/APPNAME/config/validate ディレクトリに、Action名を使った名前で作成する。10-19の例だと、Action名がsendだから validate/send.ymlとした

Redisplaying the Form / フォームの再描画

By default, symfony looks for a handleErrorSend() method in the action class whenever the validation process fails, or displays the sendError.php template if the method doesn't exist.
Validationが失敗したら、デフォルトでは、handleErrorXXXX() メソッドを実行する。handleErrorXXXX() メソッドはaction.class.phpに記述する。メソッドがないと、XXXError.php という名前のsymfony Templateファイルを探して表示する。

The usual way to inform the user of a failed validation is to display the form again with an error message. To that purpose, you need to override the handleErrorSend() method and end it with a redirection to the action that displays the form (in the example, module/index), as shown in Listing 10-20.
ユーザへValidationが失敗したことを通知するのに、ふつうは、フォームを再描画し、エラーメッセージを添えてやる。コレを実現するには、handleErrorXXX()メソッドを使う。メソッド中でRedirect処理を行う。どのようなRedirect処理をするかは、以下の例を参考に

Listing 10-20 - Displaying the Form Again, in modules/contact/actions/actions.class.php

class ContactActions extends sfActions
{
  public function executeIndex()
  {
    // Display the form
  }
 
  public function handleErrorSend()
  {
    $this->forward('contact', 'index');
  }
 
  public function executeSend()
  {
    // Handle the form submission
  }
}

と、書いてある。xxx/indexにリクエストを転送しても仕方がない。
でもどうすれば・・・

さらに読み進めてみる

If you choose to use the same action to display the form and handle the form submission, then the handleErrorSend() method can simply return sfView::SUCCESS to redisplay the form from sendSuccess.php, as shown in Listing 10-21.

フォームとフォームの送信に同じActionを再利用してフォームの描画を行いたいとき、handleErrorXXX()メソッドがsfView::SUCCSESS 戻り値として返せば、フォームの再描画をxxxSuccsess.phpのテンプレート内で処理が可能になる。

Listing 10-21 - A Single Action to Display and Handle the Form, in modules/contact/actions/actions.class.php

一つのACITONでフォームの作業を行う例。

class ContactActions extends sfActions
{
  public function executeSend()
  {
    if ($this->getRequest()->getMethod() != sfRequest::POST)
    {
      // Prepare data for the template
 
      // Display the form
      return sfView::SUCCESS;
    }
    else
    {
      // Handle the form submission
      ...
      $this->redirect('mymodule/anotheraction');
    }
  }
  public function handleErrorSend()
  {
    // Prepare data for the template
 
    // Display the form
    return sfView::SUCCESS;
  }
}

The logic necessary to prepare the data can be refactored into a protected method of the action class, to avoid repeating it in the executeSend() and handleErrorSend() methods.

この手法使うときは、データはメソッド中で保護され書き換わらない事が必要だ。handleErrorSend()やexecuteSend()で繰り返しを避けるためだ

With this new configuration, when the user types an invalid name, the form is displayed again, but the entered data is lost and no error message explains the reason of the failure. To address the last issue, you must modify the template that displays the form, to insert error messages close to the faulty field.

この手法をでは、ユーザーが間違った名前をタイプしたとき、フォームは再描画されるが、エラーメッセージが何も表示されないままだ。ユーザーにエラーの原因が伝わらない。この問題に対処するために、テンプレートを書き換えて、フォームがエラーメッセージを表示できるようにしてやる。


symfonyのフォームValidation機能をYAMLで定義する

YAMLの書き方を変えてみる。

Validationが失敗するのでYAMLの書式をTutorialの書式にしてみた。

tutorialではこういう形式になっていた validate/XXX.yml

methods:
  post:           [author, email, body]#フォームの要素
  get:            [author, email, body]#フォームの要素

fillin:
  enabled:       on #入力値を戻すかどうか

names:
  author:
    required:     Yes #必須
    required_msg: The name field cannot be left blank

  email:
    required:     No #任意
    validators:   emailValidator #emailValidatorでチェックする

  body:
    required:     Yes
    required_msg: The text field cannot be left blank

emailValidator:
  class:          sfEmailValidator #emailValidatorはsymfonyのモノを使う。
  param:
    email_error:  The email address is not valid.

でもって、エラーメッセージは prodとdevでは表示されるモノが違う。prodでは、メッセージだけ。devではValidateが失敗した箇所がメッセージに含めて表示される。


devとprodの表示の違いはハマリどころだ・・・コレまで何度もハマって来た。



泣き所

NEWするためのcreate アクションを呼び出したときやEditアクションを呼び出したとき、まだ送信される前からエラーメッセージが表示されるところだ。これはどうも出来ない。調べてみる。

追記

エラーメッセージは、入力値にエラーではなく、入力値が存在しないエラーだった。つまりYAMLの記述ミス。YAMLでフォームのINPUT名が間違っていると、Validationは当然入力エラーを返す。この辺はマジでハマリどころ。

symfonyのAdmin-generatorで作られるフォームの要素は名前が特殊

やべ。盲点だった。admin-generatorが作るフォームのname=""はphpの特殊名で配列を使ってる。これは・・・

Using Array Syntax for Form Fields

PHP allows you to use an array syntax for the form fields. When writing your own forms, or when using the ones generated by the Propel administration (see Chapter 14), you may end up with HTML code that looks like Listing 10-33.

Listing 10-33 - Form with Array Syntax

<label for="story[title]">Title:</label>
<input type="text" name="story[title]" id="story[title]" value="default value"
       size="45" />

Using the input name as is (with brackets) in a validation file will throw a parsed-induced error. The solution here is to replace square brackets [] with curly brackets {} in the fields section, as shown in Listing 10-34, and symfony will take care of the conversion of the names sent to the validators afterwards.

Listing 10-34 - Validation File for a Form with Array Syntax

fields:
  story{title}:
    required:     Yes

だそうだ。admin-generatorが使うフォームのname=MODEL_NAME[COL_NAME]になっていたので、validate/edit.ymlもそれに併せて名前を使わなくてはいけない

fileds:
  post{title}:
    required:
      msg: "title は必須です"

これで、admin-generator使ってもvalidationは出来る。あとでやり方をまとめておく。

レンタルサーバーでドメイン共有はCookieが・・・

ふと思ったこと。レンタルサーバ&ブログではドメイン共有が行われることがしばしばある。

http://xxx.s123.jp/ユーザーID/

などとしている。

これって、ドメインCookieも共有されることがあるって事だよね。

session.cookie_path = /

のときに、ハイジャックの意図無しに、セッションIDを上書きするんじゃないの?というか、意図せずにCOOKIEが読み出せてしまうのでは。レンタルサーバってドメイン共有はするべきじゃないな。多少コストがかかってもユーザー毎にサブドメインを発行するべきなのかもしれない。

などと思った。

そういえば、証明書の共有もやってたよね。どうなんだろう。。