フォームを表示するとき、ユーザーが選択可能なもののリストから選べるようにしたいことがよくあります。
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.