トップ  > メモ一覧  > カテゴリ「フォーム」の絞り込み結果 : 14件

14件中 1 〜 10 表示  1 | 2  次の4件> 最後»

No.2079 symfonyのFormで確認画面を実装する方法

symfonyのFormで確認画面を実装する方法


現在使用している旧MacBook(白)が最近スペックがとてもショボく見えてきましたので、
新しくパソコン(MacBookPro)を新調しました。
インテル製のSSDも入れ変えて快適生活に浸かっています。

さて、今回はsymfonyのFormで確認画面を挟んだシンプルな流れのシステムの作り方です。
いろいろと見てみた感じでちょっとわかりにくい感じでしたので、
最初にsymfonyを始める方のためにもまとめてみたいと思います。

まずは流れについてですが、
確認画面を挟む実装というのは「入力フォーム→確認画面→完了画面」の流れで処理を行うことです。入力画面で情報を入力し、確認画面で入力内容を確認し、完了画面を表示する流れをsymfonyのFormを使って作成していきます。

まずはサンプル用に作成するスキーマ情報は下記のような感じで作成しました。
※モデルの生成などは割愛させていただきます
サンプルにはDoctrineを使用しています

  1. Member:
  2.   actAs: { Timestampable: ~ }
  3.   tableName: member
  4.   columns:
  5.     name:         { type: string(255) }
  6.     email:        { type: string(255), notnull: true }
  7.     login_id:     { type: string(255) }
  8.     password:     { type: string(255) }

登録フォームのオブジェクトを最低限で編集します。
※ここでは最低限の処理のみの実装です
lib/form/doctrine/MemberForm.class.php
  1. <?php
  2. /**
  3.  * Member form.
  4.  */
  5. class MemberForm extends BaseMemberForm
  6. {
  7.   public function configure()
  8.   {
  9.     //フォームに使用しないフィールドをunsetする
  10.     unset(
  11.       $this['created_at'], $this['updated_at']
  12.     );
  13.   }
  14. }


次に入力フォームのアクションを作成します。
※テンプレート側の処理は画像のみとします
apps/user/modules/action/action.class.php
  1. <?php
  2.   public function executeNew(sfWebRequest $request)
  3.   {
  4.     //Form生成
  5.     $form = new MemberForm();
  6.     
  7.     //確認画面からの戻る処理
  8.     if ($request->hasParameter('back')) {
  9.       
  10.       //セッションから入力情報を取得
  11.       $values = $this->getUser()->getAttribute($form->getName());
  12.       
  13.       //Formにバインドする
  14.       $form->bind($values, $request->getFiles($form->getName()));
  15.       
  16.     //通常新規登録処理
  17.     } else {
  18.       //セッションを初期化する
  19.       $this->getUser()->setAttribute($form->getName(), null);
  20.     }
  21.     
  22.     $this->form = $form;
  23.   }

作成して実行すると下記のようなイメージになります。
※jobeetのデザインを使わせてもらいました。



次に確認画面のアクション(confirm)を実装します。
確認画面のアクションを実行されると入力された入力内容がPOSTされますので、
必ず入力内容をFromにbindしないといけません。
apps/user/modules/action/action.class.php
  1. <?php
  2.   public function executeConfirm(sfWebRequest $request)
  3.   {
  4.     //POSTではない場合は404エラーを返す
  5.     $this->forward404Unless($request->isMethod(sfRequest::POST));
  6.     
  7.     //Formを生成
  8.     $form = new MemberForm();
  9.     
  10.     //Formのbind処理
  11.     $form = $this->processForm($form, $request);
  12.     
  13.     //エラーがある場合は新規登録テンプレート適応
  14.     if (!$form->isValid()) {
  15.       $this->setTemplate('new');
  16.     }
  17.  
  18.     $this->form = $form;
  19.   }

ここで注目すべきは「$this->processForm」です。
「$this->processForm」は確認画面と登録処理実行を分けて行うメソッドとして作成したものです。
  1. 確認画面の場合:
  2. 第2引数にsfWebRequestを渡し、
  3. POSTで送られてきた入力値をFormへbindし、
  4. 送られてきた値をセッションに保存します。
  5.  
  6. 登録処理実行の場合:
  7. 第2引数にセッションに保存した入力値のデータ配列を渡し、
  8. Formへbindしてsaveを行います。

実際には下記のように実装します。
apps/user/modules/action/action.class.php
  1. <?php
  2.   private function processForm(sfForm $form, $request)
  3.   {
  4.     //確認画面用のForm処理
  5.     if ($request instanceof sfwebRequest) {
  6.       $values = $request->getParameter($form->getName());
  7.  
  8.       //フォームへバインド
  9.       $form->bind($values, array());
  10.  
  11.       //Validを通したらオブジェクト更新してセッションに保存
  12.       if ($form->isValid()) {
  13.         $form->updateObject();
  14.         sfContext::getInstance()->getUser()->setAttribute($form->getName(), $values);
  15.       }
  16.       
  17.     //登録処理用のForm処理(確認画面からは必ずPUTにする
  18.     } elseif(sfContext::getInstance()->getRequest()->isMethod(sfRequest::PUT)) {
  19.       //フォームへバインド
  20.       $form->bind($request, array());
  21.       
  22.       //Validを通したらオブジェクト更新して登録処理をしてセッションをクリア
  23.       if ($form->isValid()) {
  24.         $form->updateObject();
  25.         $form->save();
  26.         sfContext::getInstance()->getUser()->setAttribute($form->getName(), null);
  27.       }
  28.     }
  29.     
  30.     return $form;
  31.   }

確認画面のテンプレートを用意します。
確認内容の場合はフォームの入力値ではなく、
セットされたモデルのオブジェクトを利用しています。

apps/user/modules/regist/templates/confirmSuccess.php
  1. <?php use_stylesheets_for_form($form) ?>
  2. <?php use_javascripts_for_form($form) ?>
  3. <h1>New member confirm</h1>
  4. <?php $member_obj = $form->getObject() ?>
  5. <form action="<?php echo url_for('regist/create') ?>" method="post">
  6. <input type="hidden" name="sf_method" value="put" />
  7. <?php echo $form->renderHiddenFields() ?>
  8.   <table>
  9.     <tfoot>
  10.       <tr>
  11.         <td colspan="2">
  12.           <input type="button" value="戻る" onClick="location.href='<?php echo url_for('regist/new') . '?back=' ?>'"> 
  13.           <input type="submit" value="登録" />
  14.         </td>
  15.       </tr>
  16.     </tfoot>
  17.     <tbody>
  18.     <tr>
  19.       <th><label for="member_name"><?php echo $form['name']->renderLabel() ?></label></th>
  20.       <td><?php echo $member_obj->get('name') ?></td>
  21.     </tr>
  22.     <tr>
  23.       <th><label for="member_email"><?php echo $form['email']->renderLabel() ?></label></th>
  24.       <td><?php echo $member_obj->get('email') ?></td>
  25.     </tr>
  26.     <tr>
  27.       <th><label for="member_login_id"><?php echo $form['login_id']->renderLabel() ?></label></th>
  28.       <td><?php echo $member_obj->get('login_id') ?></td>
  29.     </tr>
  30.     <tr>
  31.       <th><label for="member_password"><?php echo $form['password']->renderLabel() ?></label></th>
  32.       <td><?php echo $member_obj->get('password') ?></td>
  33.     </tr>
  34.       
  35.     </tbody>
  36.   </table>
  37. </form>

実際に表示すると。。


登録処理部分の実装します。
※createアクションは表示するテンプレートはありません。
  1. 登録成功:完了画面へリダイレクトする。
  2.  
  3. 登録失敗:入力フォームを表示する。

apps/user/modules/action/action.class.php
  1. <?php
  2.   public function executeCreate(sfWebRequest $request)
  3.   {
  4.     //PUTではない場合は404エラーを返す
  5.     $this->forward404Unless($request->isMethod(sfRequest::PUT));
  6.     
  7.     //Formを生成
  8.     $form = new MemberForm();
  9.  
  10.     //bindするパラメータを取得(CSRFも取得)
  11.     $values = $this->getUser()->getAttribute($form->getName());
  12.  
  13.     //パラメータのチェック
  14.     $this->forward404Unless($values);
  15.     
  16.     //Form処理
  17.     $form = $this->processForm($form, $values);
  18.     
  19.     //エラーがなく登録が完了していたら完了画面へリダイレクト
  20.     if ($form->isValid()) {
  21.       $this->redirect('regist/complete');
  22.     } else {
  23.       $this->form = $form;
  24.       $this->setTemplate('new');
  25.     }
  26.   }

最後に完了画面のアクションとテンプレートを作って完成です。



長くなってしまいましたが、コツとしてはprocessFormメソッドで処理をまとめることと、
セッションに保存する値はフォームの入力値を保存することです。
※間違えてもセッションにFormオブジェクトは保存しないようにしましょう。

また、processFormをstaticで使用できるようにして汎用的なものにすることで、
同じような「入力フォーム→確認ページ→完了ページ」の流れを
より簡単に作成できるようになります。

引用元

更新:2010/02/11 11:31 カテゴリ: symfony  > フォーム ▲トップ

No.1978 sfForm(BaseForm)派生クラス内でのsetup()やconfigure()についてのメモ

sfForm(BaseForm)派生クラス内でのsetup()やconfigure()についてのメモ

sfFormの派生クラス、例えばモデルクラスに対応するフォームクラスなどで、setup() メソッドが使われています。Base~というクラスの setup() メソッドにデフォルトウィジェットなどが定義され、このクラスを継承するクラスの configure() メソッドでウィジェットカスタマイズしたりできます。

また、setup() メソッドの末尾には parent::setup() の呼び出しがあるので、BaseForm クラスに setup() メソッドを追加することで、すべてのフォームに共通する処理を記述することができます。


しかし、多少のルールを知っておかないと、意図したように処理されません。


setup()→configure()の順番

この順で呼び出されます。


$this->setWidgets() や $this->setValidators() はすべてを書き換える

これらのメソッドの実装コードを見ると分かりますが、それぞれウィジェットスキーマオブジェクト、バリデータースキーマオブジェクトが新規にインスタンス化され、既存のものと置き換わります。

つまり、このメソッドを呼ぶ以前にウィジェットスキーマオブジェクトに対して設定していた情報は失われるということです。

例:

  • Base~のsetup()メソッド内で、$this->setWidgets()を利用してウィジェットが設定されていて、派生クラスにもsetup()メソッドを定義して、i18n辞書を適用する場合:
<?php
  public function setup()
  {
    parent::setup();
    $this->widgetSchema->getFormFormatter()->setTranslationCatalogue('sf_guard');
  }

↑のコードだと設定が正しく反映されますが、

<?php
  public function setup()
  {
    $this->widgetSchema->getFormFormatter()->setTranslationCatalogue('sf_guard');
    parent::setup();
  }

↑のコードだと、設定が反映されません。親クラスのsetup()メソッドで、一旦ウィジェットスキーマオブジェクトが破棄されてしまうためです。

引用元

更新:2010/01/10 13:47 カテゴリ: symfony  > フォーム ▲トップ

No.1851 symfonyのフォームフィルターの活用 +α

本題に入る前に、symfonyに関連するお知らせが何点かあります。

まず、12月1日にsymfony 1.3/1.4がリリースされました!
symfony 1.3は1.2までとの互換性を保ち古い機能を残したバージョン、symfony 1.4は1.3から古い機能を削除したバージョンになります。

大きな変更点として、以下のような内容があげられます。
◆SwiftMailerメール送信ライブラリを標準で搭載
◆フォームクラスの改良
◆標準のORMがDoctrineに変更
◆Doctrineが1.0から1.2、Propelが1.3から1.4へバージョンアップ

詳しいことについては、以下のリンクをご参照ください。
symfony 1.3/1.4 の新しい機能
プロジェクトを1.2から1.3/1.4にアップグレードする

あともう1点、ぜひ見ていただきたいのが2009年のアドベントカレンダー「More with symfony」(日本名:もっと知りたいsymfony)です。昨年のJobeetのように12月1日から24日まで1日ずつ公開していく形式ですが、今回は前回と大きな違いがあります。

なんと今回のアドベントカレンダーは日本語版も同時公開となっています。実は今回僕も翻訳に参加し、11/12日目の「Doctrine の高度な使用方法、14/15日目の「Doctrine のテーブル継承の活用」の翻訳をさせていただいてます。
また、Jobeetがsymfonyのチュートリアルだったのに対し、今回のMore with symfonyはレシピブックのような形で、symfonyをすでにお使いになっている方々の様々なニーズにも応えられる内容となっています。
Jobeetも1.3/1.4用に更新されたものが公開されていますし、symfonyをまだ使ったことのない人はJobeetを、symfonyに慣れ てきてもっと色々なことが知りたい場合はMore with symfonyを、といったように、symfonyの学習の流れが出来上がってきたのかなあという感じです。

現時点ですべての章が公開されているわけではありませんが、symfonyをもっと知りたいすべての方々にお勧めできる内容ですので、ぜひぜひ見てみてください!
また、おかしな部分がありましたらこのブログのコメントでも結構ですのでご連絡いただけましたら幸いです。


さて、ここからが本題です。フォームフィルターとはsymfonyのフォームオブジェクトの検索用の拡張をしたクラスです。今回はこのフォームフィルターの仕組みや活用方法などをご紹介していきます。
前述の通りフォームフィルターはフォームの拡張です。ですので、入力フォームを制御するためのウィジェットやバリデータを管理する機構はフォームと同様に 持っており、入力フォームを作成し、入力内容のバリデーションをおこなう流れはフォームオブジェクトとほぼ同じです。また、フォームと同様に各ORMごと の拡張も用意されています。

実際には入力された値をもとにDBから取得するオブジェクトのフィルタリングを行うのが主な使い方になると思いますので、今回はsfFormFilterDoctrineに絞ってみていきます。


◆フォームフィルターを使って検索フォームを実装する

何はともあれ、フォームフィルターオブジェクトを実際に作成してみましょう。せっかくですのでsymfony 1.4で動かしていきます。まずは以下のスキーマとフィクスチャーを用意しましょう。

config/doctrine/schema.yml
  1. sfClass:
  2.   columns:
  3.     name:
  4.       type: string(255)
  5.       notnull: true
  6.     namespace:
  7.       type: string(255)
  8.  
  9. sfMethod:
  10.   columns:
  11.     class_id:
  12.       type: integer
  13.       notnull: true
  14.     name:
  15.       type: string(255)
  16.       notnull: true
  17.     description:
  18.       type: string(10000)
  19.       notnull: true
  20.   relations:
  21.     sfClass:
  22.       local: class_id
  23.       foreignAlias: sfMethods

data/fixtures/fixtures.yml
  1. sfClass:
  2.   ClassLoader:
  3.     name: ClassLoader
  4.     namespace: Symfony\Foundation
  5.   Container:
  6.     name: Container
  7.     namespace: Symfony\Components\DependencyInjection
  8.   EventDispatcher:
  9.     name: EventDispatcher
  10.     namespace: Symfony\Components\EventDispatcher
  11.  
  12.  
  13. sfMethod:
  14.   ClassLoader_method_1:
  15.     sfClass: ClassLoader
  16.     name: loadClass
  17.     description: "Loads the given class or interface."
  18.   Container_method_1:
  19.     sfClass: Container
  20.     name: setService
  21.     description: "Sets a service."
  22.   Container_method_2:
  23.     sfClass: Container
  24.     name: getService
  25.     description: "If a service is both defined through a setService() method and with a set*Service() method, the former has always precedence."
  26.   EventDispatcher_method_1:
  27.     sfClass: EventDispatcher
  28.     name: connect
  29.     description: "Connects a listener to a given event name."
  30.   EventDispatcher_method_2:
  31.     sfClass: EventDispatcher
  32.     name: notify
  33.     description: "Notifies all listeners of a given event."

今回はサンプルとして、symfonyのメソッドを検索するフォームを作ります。sfClassはクラス、sfMethodはメソッドです。
symfony 1.2まではこのスキーマとフィクスチャーを読み込んでDBへ反映と各クラスの生成をするタスクはdoctrine:build-all-reloadで した。これ以外にもdoctrine:build-で始まるタスクがいくつもあったためか、 symfony 1.3/1.4からはdoctrine:buildというタスク1つにまとめられ、--allなどのオプションを付けてビルドしたものを色々と指定できる ようになりました。

というわけで、コマンドを実行してスキーマとフィクスチャーを読み込ませましょう。databases.ymlは各環境に合わせて設定してください。今回はちょっとしたサンプルなのでSQLiteを使います。

config/databases.yml
  1. all:
  2.   doctrine:
  3.     class: sfDoctrineDatabase
  4.     param:
  5.       dsn: sqlite:/// echo realpath(dirname(__FILE__).'/..').'/data/doctrine.db' . "\n" ?>

  1. $ symfony doctrine:build --all --and-load
  2. $ symfony cc

細かいオプションなどが知りたい場合は、以下のコマンドを実行してヘルプを参照してください。

  1. $ symfony help doctrine:build

これで、データベースとテーブルの作成、モデル・フォーム・フォームフィルタークラスの作成、フィクスチャーの読み込みが行われました。毎度のことながら楽にできて助かります。
フォームフィルタークラスはlib/filter/doctrineディレクトリ内にあります。BaseFormFilterDoctrine、 sfClassFormFilter、sfMethodFormFilterの3つのクラスファイルとbaseディレクトリが含まれており、 BaseFormFilterDoctrineは共通の親クラス、sfClassFormFilterとsfMethodFormFilterはそれぞれ のモデルに対応したフォームフィルタークラスになります。

じっくりクラスファイルを見てもいいのですが、動かすほうが楽しいと思うので、一気にモジュールとアクションを作成して動かしましょう。

  1. $ symfony generate:app frontend
  2. $ symfony generate:module frontend method

generate:appタスクですが、1.3/1.4からはデフォルトでエスケーピングの有効化とCSRFトークンの指定を行ってくれるようになりました。ですのでこの状態でXSSとCSRF対策はされています。

これでmethodモジュールができたのでindexアクション内で検索ロジックを実装しましょう。

apps/frontend/modules/method/actions/actions.class.php
  1. <?php
  2. class methodActions extends sfActions
  3. {
  4.   public function executeIndex(sfWebRequest $request)
  5.   {
  6.     $this->form_filter = new sfMethodFormFilter();
  7.  
  8.     $this->methods = array();
  9.     if ($request->hasParameter($this->form_filter->getName()))
  10.     {
  11.       $this->form_filter->bind($request->getParameter($this->form_filter->getName()));
  12.       if ($this->form_filter->isValid())
  13.       {
  14.         // 入力値をもとにクエリ−を作成して、オブジェクトを取得
  15.         $this->methods = $this->form_filter->getQuery()->execute();
  16.       }
  17.     }
  18.   }
  19. }

フォームオブジェクトをご存知の方ならば見慣れたコードでしょう。フォームオブジェクトのインスタンスを作成し、リクエストからパラメータのバインドを行 う流れはフォームオブジェクトとほぼ同じです。検索なのでPOSTではなくGETで行うため、値が渡ってきているかどうかで判定しています。
フォームと違うのはフォームフィルターオブジェクトに対してgetQuery()というメソッドを発行しているところでしょう。フォームでは通常、バリデーションをパスした後にsave()メソッドを実行して、入力内容をデータベースに保存します。
フォームフィルターの目的は検索をすることなので、getQuery()メソッドを通じて検索条件が指定された状態のDoctrine_Queryオブ ジェクトを取得します。なお今回は触れませんが、Propelの場合はQueryがCriteriaになると思ってください。

残るはテンプレートです。早く動かしたいのでできる限り簡潔にします。

apps/frontend/modules/method/templates/indexSuccess.php
  1. <?php echo $form_filter->renderFormTag(url_for('method/index'), array('method' => 'get')) ?>
  2.   <table>
  3.     <tfoot>
  4.       <tr>
  5.         <td colspan="2">
  6.           <input type="submit" value="Search" />
  7.         </td>
  8.       </tr>
  9.     </tfoot>
  10.     <tbody>
  11.       <?php echo $form_filter ?>
  12.     </tbody>
  13.   </table>
  14. </form>
  15. <hr />
  16.  
  17. <?php if (count($methods)): ?>
  18.  
  19. <?php foreach ($methods as $method): ?>
  20.   <table style="margin: 10px;">
  21.     <tr>
  22.       <th>メソッド名</th>
  23.       <td>
  24.         <?php echo $method->getName() ?>
  25.       </td>
  26.     </tr>
  27.     <tr>
  28.       <th>説明</th>
  29.       <td>
  30.         <?php echo $method->getRawValue()->getDescription() ?>
  31.       </td>
  32.     </tr>
  33.     <tr>
  34.       <th>クラス</th>
  35.       <td>
  36.         <?php echo $method->getsfClass()->getQualifiedName() ?>
  37.       </td>
  38.     </tr>
  39.   </table>
  40.   <hr />
  41. <?php endforeach; ?>
  42.  
  43. <?php elseif ($form_filter->isBound() && $form_filter->isValid()): ?>
  44. 該当するメソッドはありません
  45. <?php endif; ?>

フォームフィルターはフォームを継承しているため、フォームのようにオブジェクト自身をechoすることでウィジェットのレンダリングをまとめて行えます。
後はアクションですでに取得しているメソッドオブジェクトのコレクションを表示するためのものです。
クラス名の表示をしている個所がありますが、今回は名前空間に対応したテーブル定義をおこなっているので、修飾されたクラス名を出すようにしましょう。getQualifiedName()メソッドがそれです。sfClassクラスに実装しましょう。

lib/model/doctrine/sfClass.class.php
  1. <?php
  2. class sfClass extends BasesfClass
  3. {
  4.   public function getQualifiedName()
  5.   {
  6.     $name = $this->getName();
  7.  
  8.     if (!is_null($this->getNamespace()))
  9.     {
  10.       $name = $this->getNamespace() . '\\' . $this->getName();
  11.     }
  12.  
  13.     return $name;
  14.   }
  15.  
  16.   public function __toString()
  17.   {
  18.     return $this->getQualifiedName();
  19.   }
  20. }

__toString()マジックメソッドも、修飾名を返すようにオーバーライドしておきます。さてこれでひとまず実装完了です。
ブラウザからこのアクションを確認してみると、検索フォームが出来上がっていると思います。




◆フォームフィルターの仕組みと拡張方法

フォームフィルターの内部を探るため、フォームとフォームフィルターを見比べてみましょう。
lib/filter/doctrine/base/BasesfMethodFormFilter.class.phpとlib/form/doctrine/base/BasesfMethodForm.class.phpを開いてみてください。

まずはウィジェットとバリデータの定義が違います。入力された値の使い方が違うのでこれは当然ですね。フォームフィルター側のnameと descriptionのウィジェットがsfWidgetFormFilterInputになっていますが、これはフォームフィルター用のウィジェットで す。with_emptyオプションをtrueにすると、値が空であるかどうかを判定するためのチェックボックスが付くようになります。試しに descriptionに対してwith_emptyをtrueに設定してみると、ブラウザでは以下のようになります。



(今気づいたのですが、sfMethodFormをそのままレンダリングすると、各入力フォームのname属性がsf_method[name]のように なりますね。。。これではルーティングのsf_methodとかぶるので、nameFormatを変更するなりしないといけないですね。classが予約 語なのでsfを付けてみたのですが、アダになっちゃいましたね。今回はsfMethodFormは使わないのでこのまま進めます。)

この2つのクラスファイルの中でもっとも異なる部分と言えば、フォームフィルター側にあるgetFields()メソッドの存在でしょう。

  1. <?php
  2. public function getFields()
  3. {
  4.   return array(
  5.     'id'          => 'Number',
  6.     'class_id'    => 'ForeignKey',
  7.     'name'        => 'Text',
  8.     'description' => 'Text',
  9.   );
  10. }

これはそれぞれのフィールドが、どのようにして入力された値を検索条件としてクエリ−に追加するかを指定しています。ここで使われている ForeignKey、Textなどは汎用的に使われるもので、sfFormFilterDoctrineクラスにadd{$type}Queryという メソッド名で実装されています。symfony 1.4.0では以下のようなものが実装されています。


◆addForeignKeyQuery
配列での指定の場合はIN句、単一の場合は一致しているか指定

◆addEnumQuery
一致しているか指定

◆addTextQuery
is_emptyにチェックが入っている場合はNULLかどうか、そうでなければ入力値をLIKE '%text%'形式で指定

◆addNumberQuery
is_emptyにチェックが入っている場合はNULLかどうか、そうでなければ一致しているか指定

◆addBooleanQuery
一致しているか指定

◆addDateQuery
fromとtoに対してどちらもis_emptyにチェックが入っている場合はNULLかどうか、それ以外の場合はfromに入力があればそれ以降、toに入力があればそれ以前の日付を指定

なお、TextとNumberの場合は必ずsfWidgetFormFilterInputウィジェットを、Dateの場合はsfWidgetFormFilterDateを使うようにしてください。またそれ以外ではこの2つのウィジェットは使用してはいけません。
is_emptyが入る関係で、method[name][text]に値が、method[name][is_empty]に空かどうかのフラグが入って渡されるので、うまく条件指定ができなくなってしまいます。
また、値が空の場合はそもそもメソッドが呼ばれずに処理が飛ばされます。つまり、空であることに対して処理を追加することはできません。ただし前述の通り sfWidgetFormFilterInputはテキストボックスに何も入力しない場合でも、array('text' => null)となり空とは判定されないので、拡張をする場合には注意が必要です。

ちなみにこれらのデフォルトのタイプがどのように判定されるかは、sfDoctrineFormFilterGenerator::getType($column)に記述されています。
以下のような条件でカラムの型を判定して設定しています。

  1. <?php
  2. public function getType($column)
  3. {
  4.   if ($column->isForeignKey())
  5.   {
  6.     return 'ForeignKey';
  7.   }
  8.  
  9.   switch ($column->getDoctrineType())
  10.   {
  11.     case 'enum':
  12.       return 'Enum';
  13.     case 'boolean':
  14.       return 'Boolean';
  15.     case 'date':
  16.     case 'datetime':
  17.     case 'timestamp':
  18.       return 'Date';
  19.     case 'integer':
  20.     case 'decimal':
  21.     case 'float':
  22.       return 'Number';
  23.     default:
  24.       return 'Text';
  25.   }
  26. }

ちなみにこれらの汎用的な条件指定メソッドはもちろん自分で追加することができます。自動生成したDoctrineのフォームフィルター全てで使いたい場 合はBaseFormFilterDoctrineクラスに、それぞれのクラス内でいくつも使いまわす場合はそのクラス内で、 add{$type}Queryメソッドを実装すれば、それらが使えるようになります。以下はTextの前方一致版であるTextPrefixタイプを BaseFormFilterDoctrineに実装する例です。

lib/filter/doctrine/BaseFormFilterDoctrine.class.php
  1. <?php
  2. protected function addTextPrefixQuery(Doctrine_Query $query, $field, $values)
  3. {
  4.   $fieldName = $this->getFieldName($field);
  5.  
  6.   if (is_array($values) && isset($values['is_empty']) && $values['is_empty'])
  7.   {
  8.     $query->addWhere(sprintf('%s.%s IS NULL', $query->getRootAlias(), $fieldName));
  9.   }
  10.   else if (is_array($values) && isset($values['text']) && '' != $values['text'])
  11.   {
  12.     $query->addWhere(sprintf('%s.%s LIKE ?', $query->getRootAlias(), $fieldName), $values['text'].'%');
  13.   }
  14. }

といってもaddTextQuery()をパクってきただけですが。引数として、ベースとなるクエリ−オブジェクト、フィールド名、値が渡されます。これ らをもとにしてクエリ−を変更していきます。ここで注意したいのは、クエリ−のルートとなるモデルのみにしか指定ができないことです。つまり、 sfMethodFormFilterの内部でこれらのメソッドが呼び出された場合は、sfMethodモデルのフィールドのみにしか設定ができないとい うことです。

では、たとえばsfMethodFormFilterでsfClassのnamespaceを検索対象にするにはどうすればいいのでしょうか。この場合の 問題は、(1)どうやってクエリ−オブジェクトにsfClassをJOINするか、(2)どうやってJOINしたsfClassオブジェクトの namespaceを指定するかの2点になります。

まずは(1)のクエリ−オブジェクトにJOINする方法です。そもそもデフォルトではどのようなクエリ−オブジェクトが作られているのでしょうか?これを 作成しているのは、実際にクエリーの構築を行っている、sfFormFilterDoctrine::doBuildQuery()メソッドになります。 クエリーを作成している部分をみてみましょう。

  1. <?php
  2. protected function doBuildQuery(array $values)
  3. {
  4.   $query = isset($this->options['query']) ? clone $this->options['query'] : $this->getTable()->createQuery('r');
  5.  
  6.   if ($method = $this->getTableMethod())
  7.   {
  8.     $tmp = $this->getTable()->$method($query);
  9.  
  10.     // for backward compatibility
  11.     if ($tmp instanceof Doctrine_Query)
  12.     {
  13.        $query = $tmp;
  14.     }
  15.   }
  16.  
  17.   // ...
  18. }

オプションにqueryがセットされていればそのクエリーが使われ、デフォルトでは対応するモデルのテーブルからcreateQuery()メソッドでクエリーを作成しています。デフォルトではrというエイリアスが割り当てられています。
さらに、$this->getTableMethod()からメソッド名を取得して、クエリーに処理をフックさせることも可能なようです。 getTableMethod()メソッドはオプションにtable_methodがセットされている場合はそれを取得するようです。

引用元

更新:2009/12/10 11:02 カテゴリ: symfony  > フォーム ▲トップ

No.1382 sfFormのレンダリングにカスタムフォーマッタを使用する

sfFormのレンダリングにカスタムフォーマッタを使用するAdd Star

symfonyのフォームライブラリsfFormを使ってフォームを表示する場合、フォームの各要素はデフォルトではテーブルの行として以下のようなHTMLタグで出力されます。

<tr>
  <th><label for="login_login_name">Login name</label></th>
  <td><input type="text" name="login[login_name]" id="login_login_name" /></td>
</tr>

フォーマッタは、デフォルトの「table」以外に「list」も用意されています。

(これらはそれぞれ、sfWidgetFormSchemaFormatterTableとsfWidgetFormSchemaFormatterListに対応します)

フォーマットをlistに変更するには、フォームの設定を行っている部分で以下のようにします。

<?php
class LoginForm extends sfForm {
  public function setup() {$this->widgetSchema->setDefaultFormFormatterName('list');
    :

こうすると、次のようなHTMLタグで出力されます。

<li>
  <label for="login_login_name">Login name</label>
  <input type="text" name="login[login_name]" id="login_login_name" />
</li>

出力されるタグのカスタマイズ

さて、出力されるタグを、独自クラスを指定したdivタグに変更する場合はどうするのでしょうか。

公式のドキュメント(Forms in Action)では、「テンプレートのカスタマイズ」のやり方しか書かれていません。

この方法ではテンプレートに直接フォーム要素を埋め込んでいくので、デザインに対しては柔軟になるのですが、フォーム要素を追加したり変更したりする場合にはテンプレートも合わせて修正しなくてはならないので、開発効率はかなり低下します。


フォーマッタの出力とテンプレートのカスタマイズの中間くらいのことをやりたいですよね。


そこで、sfWidgetFormSchemaFormatterListクラスを真似た、myWidgetFormSchemaFormatterDivクラスを次のように作成します。

<?php
class myWidgetFormSchemaFormatterDiv extends sfWidgetFormSchemaFormatter
{
  protected
    $rowFormat       = "<div class=\"formrow\">%error%%label%%field%%help%%hidden_fields%</div>\n",
    $errorRowFormat  = "<div class=\"error\">%errors%</div>\n",
    $helpFormat      = '<div class="help">%help%</div>',
    $decoratorFormat = "<div class=\"formcontent\">\n%content%\n</div>\n";
}

このフォーマッタを使用するために、formクラスのsetupメソッドで、次のようにフォーマッタを追加してデフォルトに設定します。

<?php
class LoginForm extends sfForm
{
  public function setup()
  {$this->widgetSchema->addFormFormatter('div', new myWidgetFormSchemaFormatterDiv($this->widgetSchema));
    $this->widgetSchema->setDefaultFormFormatterName('div');
    :

こうすると、myWidgetFormSchemaFormatterDivクラスのフォーマット設定でフォームがレンダリングされます。

<div class="formrow"><label for="login_login_name">Login name</label>
<input type="text" name="login[login_name]" id="login_login_name" /></div>

sfWidgetFormSchema::setFormFormatterNameを使用する場合の注意

上のサンプルコードではsetDefaultFormFormatterNameメソッドでフォーマッタを変更しましたが、同じような機能を持つsetFormFormatterNameメソッドがあります。

こ の2つの違いは、クラスのスタティック変数としてフォーマッタ名を保存するか、インスタンスのメンバ変数として保存するかです。1つの画面で複数のフォー ムオブジェクトを使用し、それぞれで別のフォーマッタを利用したい場合は、setFormFormatterNameを使用するということですね。

引用元

更新:2009/08/26 09:19 カテゴリ: symfony  > フォーム ▲トップ

No.1063 save( )の流れ :updateObjectの記述

save

updateObject

保存の実行

※保存の前にやりたいことを「updateObject」クラスに書くべし!
更新:2009/06/18 17:29 カテゴリ: symfony  > フォーム ▲トップ

No.1062 ファイルアップロードでファイルをアップロードしないとき

以下の記述がないと
----------------------------------------------------
500 | Internal Server Error | Doctrine_Validator_Exception
Validation failed in class MessageFile

1 field had validation error:

* 1 validator failed on file_id (notnull)
----------------------------------------------------
といわれる;;

abstract class PluginDiaryForm extends BaseDiaryForm{
  public function updateObject($values = null)
  {
    $object = parent::updateObject($values);
  
    foreach ($this->embeddedForms as $key => $form)
    {
      if (!($form->getObject() && $form->getObject()->getFile()))
      {
        unset($this->embeddedForms[$key]);
      }
    }

  
    return $object;
  }
}

つまり、save()の前にアンセットする必要があるとのこと
(しないとNotNull属性エラーでっせ)
更新:2009/06/18 17:26 カテゴリ: symfony  > フォーム ▲トップ

No.986 The symfony Forms Book 1.2

The symfony Forms Book 1.2

ライセンス:GFDL

原文の最新バージョンはhttp://www.symfony-project.org/book/forms/1_2/を参照して下さい。

第1章 - フォームの作成
1.1. 始める前に
1.2. ウィジェット
1.2.1. sfFormクラスとsfWidgetクラス
1.2.2. フォームを表示する
1.2.3. ラベル
1.2.4. 生成されたテーブルを越えて
1.2.5. フォームを投稿する
1.2.6. 別の解決方法
1.3. ウィジェットを設定する
1.3.1. ウィジェットのオプション
1.3.2. ウィジェットのHTML属性
1.3.3. フィールドに対してデフォルトの値を定義する
第2章 - フォームのバリデーション
2.1. 始める前に
2.2. バリデータ
2.2.1. sfValidatorBaseクラス
2.2.2. バリデータの目的
2.2.3. 無効なフォーム
2.3. バリデータのカスタマイズ
2.3.1. エラーメッセージをカスタマイズする
2.4. バリデータのセキュリティ
2.5. 論理バリデータ
2.6. グローバルバリデータ
2.7. ファイルのアップロード
第3章 - ウェブデザイナーのためのフォーム
3.1. 始める前に
3.2. プロトタイプのテンプレート
3.3. プロトタイプのテンプレートのカスタマイズ
3.4. 表示のカスタマイズ
3.4.1. フィールド上でrenderRow()メソッドを利用する
3.4.2. フィールド上でrender()メソッドを利用する
3.4.3. フィールド上でrenderLabel()メソッドを利用する
3.4.4. フィールド上でrenderError()メソッドを利用する
3.4.5. エラーメッセージのきめ細かいカスタマイズ
3.4.6. 隠しフィールドを扱う
3.4.7. グローバルエラーを扱う
3.5. 国際化
3.6. 開発者と交流する
第4章 - Propelとの統合
4.1. 始める前に
4.2. フォームクラスを生成する
4.3. CRUDジェネレータ
4.4. 生成されたフォームをカスタマイズする
4.4.1. バリデータとウィジェットを設定する
4.4.2. バリデータを変更する
4.4.3. バリデータを追加する
4.4.4. ウィジェットを変更する
4.4.5. フィールドを削除する
4.4.6. まとめ
4.5. フォームのシリアライズ
4.5.1. デフォルトの値
4.5.2. ライフサイクルを扱う
4.5.3. Propelオブジェクトを作成して修正する
4.5.4. save()メソッド
4.5.5. ファイルのアップロードを扱う
4.5.6. save()メソッドをカスタマイズする
4.5.7. doSave()メソッドをカスタマイズする
4.5.8. updateObject()メソッドをカスタマイズする
第8章 - 国際化とローカライズ
5.1. フォームの国際化
5.1.1. 翻訳のために使うカタログを指定する
5.1.2. エラーメッセージの国際化
5.2. 翻訳オブジェクトのカスタマイズ
5.2.1. パラメータとして受容できる翻訳のcallable
5.3. Propelのオブジェクトの国際化
5.4. ローカライズされたウィジェット
5.4.1. 日付セレクタ
5.4.2. 国セレクタ
5.4.3. cultureセレクタ
第11章 - Doctrineとの統合
6.1. 始める前に
6.2. フォームクラスを生成する
6.3. CRUDジェネレータ
6.4. 生成フォームをカスタマイズする
6.4.1. バリデータとウィジェットを設定する
6.4.2. バリデータを変更する
6.4.3. バリデータを追加する
6.4.4. ウィジェットを変更する
6.4.5. フィールドを削除する
6.4.6. 要約
6.5. フォームのシリアライズ
6.5.1. デフォルトの値
6.5.2. ライフサイクルに対処する
6.5.3. Doctrineオブジェクトを作り修正する
6.5.4. save()メソッド
6.5.5. ファイルのアップロードを扱う
6.5.6. save()メソッドをカスタマイズする
6.5.7. doSave()メソッドをカスタマイズする
6.5.8. updateObject()メソッドをカスタマイズする
付録A - ウィジェット
7.1. 紹介
7.1.1. `sfWidget`基底クラス
7.1.2. `sfWidgetForm`基底クラス
7.1.3. ウィジェットスキーマ
7.2. ウィジェット
7.3. 入力ウィジェット
7.3.1. ~`sfWidgetFormInput`~
7.3.2. ~`sfWidgetFormInputCheckbox`~
7.3.3. ~`sfWidgetFormInputHidden`~
7.3.4. ~`sfWidgetFormInputPassword`~
7.3.5. ~`sfWidgetFormInputFile`~
7.3.6. ~`sfWidgetFormInputFileEditable`~
7.3.7. ~`sfWidgetFormTextarea`~
7.3.8. ~`sfWidgetFormTextareaTinyMCE`~
7.4. 選択ウィジェット
7.4.1. 選択の表現
7.4.2. 選択のグループ化
7.4.3. サポートされるオプション
7.4.4. 二重リストの表現
7.4.5. 自動入力補完
7.4.6. Propelモデルにバインドされた選択
7.4.7. Doctrineモデルにバインドされた選択
7.5. 日付ウィジェット
7.5.1. ~`sfWidgetFormDate`~
7.5.2. ~`sfWidgetFormTime`~
7.5.3. ~`sfWidgetFormDateTime`~
7.5.4. ~`sfWidgetFormI18nDate`~
7.5.5. ~`sfWidgetFormI18nTime`~
7.5.6. ~`sfWidgetFormI18nDateTime`~
7.5.7. ~`sfWidgetFormDateRange`~
7.5.8. ~`sfWidgetFormJQueryDate`~
7.6. 国際化ウィジェット
7.6.1. ~`sfWidgetFormI18nSelectCountry`~
7.6.2. ~`sfWidgetFormI18nSelectLanguage`~
7.6.3. ~`sfWidgetFormI18nSelectCurrency`~
7.7. Captchaウィジェット
7.8. フィルタウィジェット
7.8.1. ~`sfWidgetFormFilterInput`~
7.8.2. ~`sfWidgetFormFilterDate`~
7.9. ~`sfWidgetFormSchema`~
7.9.1. `setLabel()`、`getLabel()`、`setLabels()`、`getLabels()`
7.9.2. `setDefault()`、`getDefault()`、`setDefaults()`、`getDefaults()`
7.9.3. `setHelp()`、`setHelps()`、`getHelps()`、`getHelp()`
7.9.4. `getPositions()`、`setPositions()`、`moveField()`
7.9.5. ~`sfWidgetFormSchemaDecorator`~
付録B - バリデータ
8.1. 紹介
8.1.1. `sfValidatorBase`基底クラス
8.1.2. バリデータスキーマ
8.2. バリデータ
8.3. シンプルなバリデータ
8.3.1. `sfValidatorString`
8.3.2. `sfValidatorRegex`
8.3.3. `sfValidatorEmail`
8.3.4. `sfValidatorUrl`
8.3.5. `sfValidatorInteger`
8.3.6. `sfValidatorNumber`
8.3.7. `sfValidatorBoolean`
8.3.8. `sfValidatorChoice`
8.3.9. `sfValidatorPass`
8.3.10. `sfValidatorCallback`
8.4. 日付バリデータ
8.4.1. `sfValidatorDate`
8.4.2. `sfValidatorTime`
8.4.3. `sfValidatorDateTime`
8.4.4. `sfValidatorDateRange`
8.5. ファイルバリデータ
8.5.1. `sfValidatorFile`
8.6. 論理バリデータ
8.6.1. `sfValidatorAnd`
8.6.2. `sfValidatorOr`
8.6.3. `sfValidatorSchema`
8.6.4. `sfValidatorSchemaCompare`
8.6.5. `sfValidatorSchemaFilter`
8.7. 国際化バリデータ
8.7.1. `sfValidatorI18nChoiceCountry`
8.7.2. `sfValidatorI18nChoiceLanguage`
8.8. Propelバリデータ
8.8.1. `sfValidatorPropelChoice`
8.8.2. `sfValidatorPropelChoiceMany`
8.8.3. `sfValidatorPropelUnique`
8.9. Doctrineバリデータ
8.9.1. `sfValidatorDoctrineChoice`
8.9.2. `sfValidatorDoctrineChoiceMany`
8.9.3. `sfValidatorDoctrineUnique`

引用元

更新:2009/06/02 09:43 カテゴリ: symfony  > フォーム ▲トップ

No.984 フォームのアクション

◆例
// apps/frontend/modules/job/actions/actions.class.php
public function executeNew(sfWebRequest $request)
{
  $this->form = new JobeetJobForm();
}
 
public function executeCreate(sfWebRequest $request)
{
  $this->form = new JobeetJobForm();
  $this->processForm($request, $this->form);
  $this->setTemplate('new');
}
 
public function executeEdit(sfWebRequest $request)
{
  $this->form = new JobeetJobForm($this->getRoute()->getObject());
}
 
public function executeUpdate(sfWebRequest $request)
{
  $this->form = new JobeetJobForm($this->getRoute()->getObject());
  $this->processForm($request, $this->form);
  $this->setTemplate('edit');
}
 
public function executeDelete(sfWebRequest $request)
{
  $request->checkCSRFProtection();
 
  $job = $this->getRoute()->getObject();
  $job->delete();
 
  $this->redirect('job/index');
}
 
protected function processForm(sfWebRequest $request, sfForm $form)
{
  $form->bind(
    $request->getParameter($form->getName()),
    $request->getFiles($form->getName())
  );
 
  if ($form->isValid())
  {
    $job = $form->save();
 
    $this->redirect($this->generateUrl('job_show', $job));
  }
}



引用元

更新:2009/06/02 09:40 カテゴリ: symfony  > フォーム ▲トップ

No.983 フォームのテンプレート

フォームのテンプレート

フォームがカスタマイズされたので、表示する必要があります。 フォーム用の~テンプレート~は新しい求人を作るもしくは既存のものを作りたい場合は同じです。 実際、newSuccess.phpeditSuccess.phpテンプレートの両方は とても似通っています:

<!-- apps/frontend/modules/job/templates/newSuccess.php -->
<?php use_stylesheet('job.css') ?>
 
<h1>Post a Job</h1>
 
<?php include_partial('form', array('form' => $form)) ?>
◆フォームテンプレートのカスタマイズ

フォーム自身は_form ~パーシャル~でレンダリングされます。 生成された_formパーシャルの内容を次のコードで置き換えます:

<!-- apps/frontend/modules/job/templates/_form.php -->
<?php include_stylesheets_for_form($form) ?>
<?php include_javascripts_for_form($form) ?>
 
<?php echo form_tag_for($form, '@job') ?>
  <table id="job_form">
    <tfoot>
      <tr>
        <td colspan="2">
          <input type="submit" value="Preview your job" />
        </td>
      </tr>
    </tfoot>
    <tbody>
      <?php echo $form ?>
    </tbody>
  </table>
</form>

include_javascripts_for_form()include_stylesheets_for_form()ヘルパーは フォームウィジェットに 必要なJavaScriptとスタイルシートをインクルードします。


form_tag_for()ヘルパーは渡されたフォームとルート用の<form>タグを生成し オブジェクトが新しいかそうではないかによって POSTもしくはPUTへの~HTTPメソッド~を変更します。 フォームがファイル入力タグを持つ場合これはmultipart属性も考慮します。

結局のところ、<?php echo $form ?>はフォームウィジェットをレンダリングします。

フォームのアクション

フォームクラスをこれをレンダリングするテンプレートが用意されました。 では、これを~アクション~と実際に連携させてみましょう。

求人のフォームはjobモジュールの5つのメソッドで管理されます:

  • new: 新しい求人を作成する空白のフォームを表示する
  • edit: 既存の求人を編集するフォームを表示する
  • create: ユーザー投稿の値で新しい求人を作成する
  • update: ユーザー投稿の値で既存の求人を更新する
  • processForm: createupdateによって呼び出されフォームを処理する (バリデーション、フォームの再投入、 とデータベースへのシリアライズ)



引用元

更新:2009/06/02 09:38 カテゴリ: symfony  > フォーム ▲トップ
14件中 1 〜 10 表示  1 | 2  次の4件> 最後»

FuelPHP

Mac

web開発

プロマネ

マネタイズ

プレゼン

webサービス運用

webサービス

Linux

サーバ管理

MySQL

ソース・開発

svn・git

PHP

HTML・CSS

JavaScript

ツール, ライブラリ

ビジネス

テンプレート

負荷・チューニング

Windows

メール

メール・手紙文例

CodeIgniter

オブジェクト指向

UI・フロントエンド

cloud

マークアップ・テキスト

Flash

デザイン

DBその他

Ruby

PostgreSQL

ユーティリティ・ソフト

Firefox

ハードウェア

Google

symfony

OpenPNE全般

OpenPNE2

Hack(賢コツ)

OpenPNE3

リンク

個人開発

その他

未確認

KVS

ubuntu

Android

負荷試験

オープンソース

社会

便利ツール

マネー

Twig

食品宅配

WEB設計

オーディオ

一般常識

アプリ開発

サイトマップ

うずら技術ブログ

たませんSNS

rss2.0