フォームを表示するとき、ユーザーが選択可能なもののリストから選べるようにしたいことがよくあります。
HTMLにおいて、選択肢はselectタグによって表現されます:

複数の選択を受け入れられるようにmultiple属性を追加できます:

sfWidgetFormChoice
選択はラジオボタンのリスト(単独の選択)もしくはチェックボックスのリスト(複数の選択)でも表現できます。
これらすべての実現方法を統一するために、symfony 1.2はsfWidgetFormChoiceと呼ばれる新しいウィジェットを搭載しています。
sfWidgetFormChoiceはレンダリングを別のウィジェット(レンダラウィジェット)に委譲する抽象ウィジェットです。
可能な組み合わせを描くためにシンプルな例を取り上げてみます。 プロジェクトにおいて、次のスキーマがあるとします:

// config/schema.yml
propel:
demo_article:
id: ~
author_id: { type: integer, foreignReference: id, foreignTable: demo_author, onDelete: cascade, onUpdate: cascade, required: true }
status: varchar(255)
title: varchar(255)
content: longvarchar
published_at: timestamp
demo_category:
id: ~
name: varchar(255)
demo_author:
id: ~
name: varchar(255)
demo_tag:
id: ~
name: varchar(255)
demo_tag_article:
tag_id: { type: integer, primaryKey: true, foreignReference: id, foreignTable: demo_tag, onDelete: cascade, onUpdate: cascade, required: true }
article_id: { type: integer, primaryKey: true, foreignReference: id, foreignTable: demo_article, onDelete: cascade, onUpdate: cascade, required: true }
demo_category_article:
category_id: { type: integer, primaryKey: true, foreignReference: id, foreignTable: demo_category, onDelete: cascade, onUpdate: cascade, required: true }
article_id: { type: integer, primaryKey: true, foreignReference: id, foreignTable: demo_article, onDelete: cascade, onUpdate: cascade, required: true }
これらはシンプルなCMS用の古典的なスキーマです。
記事は1人の著者を持ち、たくさんのタグとカテゴリを持つことができます。
それぞれの記事は1つのステータス: published、draftもしくはdeletedを持つことができます。
ステータスを保存するためのテーブルは作られていないので、ステータスの値はプレーンテキストとして保存されます。
基本的なCRUDオペレーションを提供するモジュールを作ってDemoArticleモデルを遊んでみましょう:
$ php symfony propel:build-all $ php symfony propel:generate-module frontend article DemoArticle
編集ページに移動すると、次のようなフォームが表示されます:

DemoArticleモデル (lib/form/base/BaseDemoArticle.class.php)用の生成フォームクラスを見てみると、
symfonyはauthor_idに対してsfWidgetFormPropelChoiceウィジェット、
demo_category_article_listとdemo_tag_article_listに対してsfWidgetFormPropelChoiceManyウィジェット
を使用していることがわかります。
symfonyはスキーマの定義に基づいて使う最良のウィジェットを推測しました。
sfWidgetFormPropelChoiceはPropelオブジェクトに基づいた単独の選択を表しsfWidgetFormPropelChoiceManyはPropelオブジェクトに基づいた複数選択を表します。
フォームをカスタマイズする
フォームをカスタマイズするために最初にできることはstatusウィジェットを選択に変換することです:

最初に、DemoArticlePeerモデルクラスでステータスを定義する必要があります:
// lib/model/DemoArticlePeer.php class DemoArticlePeer extends BaseDemoArticlePeer { static protected $choices = array( 'published' => 'published', 'draft' => 'draft', 'deleted' => 'deleted' ); static public function getStatusChoices() { return self::$choices; } }
それから、ウィジェットとstatusに関連づけされたバリデータを変更するためにDemoArticleFormクラスを編集します:
// lib/form/DemoArticleForm.class.php class DemoArticleForm extends BaseDemoArticleForm { public function configure() { $this->widgetSchema['status'] = new sfWidgetFormChoice(array( 'choices' => DemoArticlePeer::getStatusChoices() )); $this->validatorSchema['status'] = new sfValidatorChoice(array( 'choices' => array_keys(DemoArticlePeer::getStatusChoices()) )); } }
sfWidgetFormChoiceはselectタグで使用する選択の配列をchoicesオプションとして受け取ります。
sfValidatorChoiceはchoicesオプションも受け取ります。これはstatusカラムに対して有効な値です(配列DemoArticlePeer::getStatusChoices()のキー)。
選択肢で遊ぶ
ラジオボタンのリスト
sfWidgetFormChoiceウィジェットで少し遊んでみましょう!
以前のスクリーンショットで見ることができるように、ステータスはselectタグで表現できます。
ステータス用の値の数は少ないので、ステータスをラジオボタンのリストとして表示する方がベターです:

これを実現するのはとても簡単です。
sfWidgetFormChoiceはselectタグからの出力をラジオボタンのリストに変換するexpandedオプションを受け取ります:
$this->widgetSchema['status'] = new sfWidgetFormChoice(array( 'choices' => DemoArticlePeer::getStatusChoices(), 'expanded' => true, ));
チェックボックスリスト
カテゴリのリストもとても小さいので、それらをチェックボックスのリストとして表示するほうがよいです:

単独選択に使用してきたexpandedオプションは複数選択のウィジェットにも使用できます。
ウィジェットは基底フォームクラスで生成され変更を必要としないのでexpandedオプションをtrueにセットするだけです:
$this->widgetSchema['demo_category_article_list']->setOption('expanded', true);
要約
次のテーブルはsfWidgetFormChoiceとレンダラーウィジェットの異なる設定を要約しています:
sfWidgetFormChoice |
expandedがfalse |
expandedがtrue |
|---|---|---|
multipleがfalse |
sfWidgetFormSelect |
sfWidgetFormSelectRadio |
multipleがtrue |
sfWidgetFormSelectMany |
sfWidgetFormSelectCheckbox |
同じスクリーンショットつきの同じテーブル:
sfWidgetFormChoice |
expandedが false |
expandedがtrue |
|---|---|---|
multipleがfalse |
![]() |
![]() |
multipleがtrue |
![]() |
![]() |
選択のグループを作成する
selectタグのあまり知られていない機能の1つはoptgroup機能で選択のグループを作成できる方法です:

sfWidgetFormChoiceファミリーウィジェットはグループ用の組み込みのサポート機能を持ちます。
必要なのはchoicesオプションに対して配列の配列を渡すことだけです:
$choices = array( 'Europe' => array('France' => 'France', 'Spain' => 'Spain', 'Italy' => 'Italy'), 'America' => array('USA' => 'USA', 'Canada' => 'Canada', 'Brazil' => 'Brazil'), ); $this->widgetSchema['country'] = new sfWidgetFormChoice(array('choices' => $choices));
もちろん、ラジオボタンのリストを展開できます:
$this->widgetSchema['country'] = new sfWidgetFormChoice(array( 'choices' => $choices, 'expanded' => true, ));

レンダラウィジェットによって使用されるレイアウトも使用できます:
$this->widgetSchema['country'] = new sfWidgetFormChoice(array( 'choices' => $choices, 'expanded' => true, 'renderer_options' => array('template' => '<strong>%group%</strong> %options%'), ));

そしてもちろん、multipleオプションに対しても機能します:


さらにJavaScript
これまでの作業は簡単でした。さらなる可能性を探るためにJavaScriptを追加してみましょう。
二重リスト
CMSが大規模で使われる場合、タグがますます増え、現在の記事に関連したタグを見つけるのがより困難になります。 このような状況に対して、二重リストウィジェットはベストの解決方法の1つです:

これまでのところ、シンプルな設定(multipleと expanded)に基づいてsymfonyはベストなウィジェットを選択してきました。
しかしsfWidgetFormChoiceはselectタグウィジェットをそのまま二重リストとしてレンダリングできません。
幸いにして、我々はsfWidgetFormChoiceがレンダリング作業を別のウィジェットに委譲することを知っています。
レンダリングウィジェットの変更するにはrenderer_classオプションを修正します。
sfFormExtraPluginをインストールする場合、
とても便利で興味深いウィジェットとバリデータが見つかりますが、サードパーティのライブラリに依存しているので
コアに取り込まれていません。
sfWidgetFormSelectDoubleListウィジェットはそれらの1つです:
$this->widgetSchema['demo_tag_article_list']->setOption('renderer_class', 'sfWidgetFormSelectDoubleList');
ページをリフレッシュすると、ウィジェットがJavaScriptに依存しているので正しく動作しません。 ウィジェットのAPIドキュメントには適切に設定する必要がある内容が書かれています:
// apps/frontend/modules/article/templates/_form.php <?php use_javascript('/sfFormExtraPlugin/js/double_list.js') ?> <form action="<?php echo url_for('@article_update') ?>"> <table> <?php echo $form ?> <!-- ... --> </table> </form>
自動入力補完
まだauthor_idフィールドを遊んでいませんでした。本当にCMSにたくさんの筆者がいる場合を想像してみましょう。
ドロップダウンのselectタグでとても長い名前リストで何かを見つけるのは簡単なことではありません。
ですので、これを自動入力補完ウィジェットに変換してみましょう。



感動的だと思いませんか?動作させるためには、以前よりも少し多くの作業をしなければなりません。
sfFormExtraPluginはJQueryライブラリに基づいた2つの自動入力ウィジェットを含みます:
sfWidgetFormJQueryAutocompleter: 任意の自動入力補完タスク用に使用できるsfWidgetFormPropelJQueryAutocompleter: Propel用に最適化された自動入力補完機能
我々の状況において、Propel用のウィジェットを利用します:
// lib/form/DemoArticleForm.class.php $this->widgetSchema['author_id']->setOption('renderer_class', 'sfWidgetFormPropelJQueryAutocompleter'); $this->widgetSchema['author_id']->setOption('renderer_options', array( 'model' => 'DemoAuthor', 'url' => $this->getOption('url'), ));
renderer_optionsを設定することでウィジェットにいくつかのオプションを渡しました。
これらのオプションにおいて、urlがurlフォームオプション($this->getOption('url'))に設定されていることにお気づきかもしれません。
フォームインスタンスを作るとき、最初のコンストラクタの最初の引数はデフォルト値で2番目の引数はオプションの配列です:
public function executeEdit($request) { // ... $this->form = new DemoArticleForm($article, array('url' => $this->getController()->genUrl('article/ajax'))); // ... }
article/ajaxアクションを作る必要があります。
ウィジェットがこのアクションを呼び出すとき、いくつかのリクエストパラメータを渡します:
q: ユーザーが入力した文字列limit: 返す項目の最大数
コードは次のとおりです:
// apps/frontend/modules/article/actions/actions.class.php public function executeAjax($request) { $this->getResponse()->setContentType('application/json'); $authors = DemoAuthorPeer::retrieveForSelect($request->getParameter('q'), $request->getParameter('limit')); return $this->renderText(json_encode($authors)); } // lib/model/DemoAuthorPeer.php class DemoAuthorPeer extends BaseDemoAuthorPeer { static public function retrieveForSelect($q, $limit) { $criteria = new Criteria(); $criteria->add(DemoAuthorPeer::NAME, '%'.$q.'%', Criteria::LIKE); $criteria->addAscendingOrderByColumn(DemoAuthorPeer::NAME); $criteria->setLimit($limit); $authors = array(); foreach (DemoAuthorPeer::doSelect($criteria) as $author) { $authors[$author->getId()] = (string) $author; } return $authors; } }
すべてのJavaScriptウィジェットに関して、適切に動作させるためにフォームテンプレートにファイルを追加することも必要です:
// apps/frontend/modules/article/templates/_form.php <?php use_javascript('/sfFormExtraPlugin/js/jquery.autocompleter.js') ?> <?php use_stylesheet('/sfFormExtraPlugin/css/jquery.autocompleter.css') ?> <!-- ... -->
作業は終わりました。 著者の名前を表示し、著者のidをフォームに投稿できる自動入力補完ウィジェットが手に入りました。 バリデーターのおかげで、有効なidのみが投稿されデータベースに保存されることを保証できます。
完成品のフォーム
完成品のフォームは次のとおりです。 選択に関してユーザーに質問をする方法をすべて示しています:

これはたった1つのウィジェットによる柔軟性のおかげです!
This work is licensed under the Creative Commons Attribution-Noncommercial-No Derivative Works 3.0 Unported License license.



