symfony Forms in Action

第3章 - Web デザイナのためのフォーム

You are currently browsing
the website for symfony 1

Visit the Symfony2 website


About

You are currently reading "symfony Forms in Action" which is licensed under the Creative Commons Attribution-Share Alike 3.0 Unported License license.

Master symfony

Be trained by SensioLabs experts (2 to 6 day sessions -- French or English).
trainings.sensiolabs.com

Books on symfony

Learn more about symfony with the official guides.
books.sensiolabs.com

L'audit Qualité par SensioLabs

200 points de contrôle de votre applicatif web.
audit.sensiolabs.com

Chapter Content

はじめる前に

プロトタイプのテンプレート

プロトタイプのテンプレートのカスタマイズ

表示のカスタマイズ

フィールド上で renderRow() メソッドを利用する

フィールド上で render() メソッドを利用する

フィールド上で renderLabel() メソッドを利用する

フィールド上で renderError() メソッドを利用する

エラーメッセージのきめ細かいカスタマイズ

隠しフィールドを扱う

グローバルエラーを扱う

国際化

開発者と交流する

symfony training
Be trained by symfony experts
May 29: Paris (Web Development with Symfony2 - Français)
May 31: Paris (Mastering Symfony2 - Français)
Jun 06: Paris (Introduction to Symfony2 - Français)
Jun 06: Paris (Introduction to Symfony2 - English)
Jun 06: Paris (Going Further with Symfony2 - English)
and more...

Search


powered by google
You are currently browsing "symfony Forms in Action" in Japanese for the 1.4 version - Switch to version: - Switch to language:
Creative Commons License This work is licensed under a Creative Commons Attribution-Share Alike 3.0 Unported License.

1章と2章でウィジェットとバリデーションのルールを作る方法を見ました。これらを表示するために <?php echo $form ?> ステートメントを使いました。このステートメントのおかげで開発者は最終的にどのように表示されるのか考えなくてもアプリケーションのコードを書けます。フィールド (名前、ウィジェット・・・) を変更もしくは追加するたびにテンプレートを変更する必要はありません。開発者がモデルとビジネスロジックに重点的に取り組まなければならないとき、このステートメントは試作と初期の開発フェーズに適しています。

いったんオブジェクトモデルが安定してスタイルのガイドラインの準備ができれば、Web デザイナはさまざまなアプリケーションのフォームの作業に戻り、デザインを整えることができます。

この章を始める前に、symfony のテンプレートシステムとビューレイヤーを熟知していなければなりません。そのためには、「A Gentle Introduction to symfony」 の「ビューレイヤーの内側」の章をご覧ください。

symfony のフォームシステムは MVC モデルにしたがって開発されます。 MVC パターンの助けによって開発チームのすべてのタスクは分離されます: 開発者はフォームを作りそれらのライフサイクルに対処し、Web デザイナーはそれらを整え飾りつけます。関心事の分離 (separation of concerns) はプロジェクトチーム内のコミュニケーションの置き換えになることはありません。

はじめる前に

1章と2章で作り込んだコンタクトフォームを復習します (図3-1)。この章だけを読む Web デザイナのための技術情報の要約は次のようになります。

この章ではフォームを表示するために使ったプロトタイプのテンプレートをカスタマイズする方法をできるかぎり多く示すことを目的とします (リスト3-1)。

図3-1 - 問い合わせフォーム

問い合わせフォーム

リスト3-1 - 問い合わせフォームを表示するプロトタイプのテンプレート

// apps/frontend/modules/contact/templates/indexSuccess.php
<form action="<?php echo url_for('contact/index') ?>" method="POST">
  <table>
    <?php echo $form ?>
    <tr>
      <td colspan="2">
        <input type="submit" />
      </td>
    </tr>
  </table>
</form>

プロトタイプのテンプレート

今のところ、フォームの表示に必要な HTML を自動的に生成するためにプロトタイプのテンプレートで <?php echo $form ?> ステートメントを使いました。

フォームはフィールドで構成されます。テンプレートレベルでは、それぞれのフィールドは3つの要素で構成されます。

リスト3-2で示されるように、無効な投稿に対して <?php echo $form ?> ステートメントはこれらすべての要素を自動的に生成します。

リスト3-2 - 無効な投稿の場合に生成されるテンプレート

<form action="/frontend_dev.php/contact" method="POST">
  <table>
    <tr>
      <th><label for="contact_name">Name</label></th>
      <td><input type="text" name="contact[name]" id="contact_name" /></td>
    </tr>
    <tr>
      <th><label for="contact_email">Email</label></th>
      <td>
        <ul class="error_list">
          <li>This email address is invalid.</li>
        </ul>
        <input type="text" name="contact[email]" value="fabien" id="contact_email" />
      </td>
    </tr>
    <tr>
      <th><label for="contact_subject">Subject</label></th>
      <td>
        <select name="contact[subject]" id="contact_subject">
          <option value="0" selected="selected">Subject A</option>
          <option value="1">Subject B</option>
          <option value="2">Subject C</option>
        </select>
      </td>
    </tr>
    <tr>
      <th><label for="contact_message">Message</label></th>
      <td>
        <ul class="error_list">
          <li>The message "foo" is too short. It must be of 4 characters at least.</li>
        </ul>
        <textarea rows="4" cols="30" name="contact[message]" id="contact_message">foo</textarea>
      </td>
    </tr>
    <tr>
      <td colspan="2">
        <input type="submit" />
      </td>
    </tr>
  </table>
</form>

開きフォームタグを生成する追加のショートカット: echo $form->renderFormTag(url_for('contact/index')) が用意されています。配列を提供することでフォームタグにたくさんの追加属性をより楽に渡すことも可能になります。このショートカットを利用する際の不利な点はデザインツールがフォームを適切に検出しずらくなることです。

このコードを分解してみましょう。図3-2はそれぞれのフィールドに対して生み出された <tr> の列を強調しています。

図3-2 - フィールドによるフォームの分割

フィールドによるフォームの分割

HTML コードの3つのピースはそれぞれのフィールドに対して生成され (図3-3)、フィールドの3つの要素に対応しています。email フィールドに対して生成された HTML コードは次のようになります。

図3-3 - email フィールドの分解

email フィールドの分解

すべてのフィールドに id 属性が用意され、これによって開発者が CSS もしくは JavaScript のふるまいをかんたんに追加できます。

プロトタイプのテンプレートのカスタマイズ

コンタクトフォームのようなシンプルなフォームに対しては <?php echo $form ?> ステートメントでじゅうぶんです。そして、当然ながら、これは <?php echo $form->render() ?> ステートメントの単なるショートカットです。

render() メソッドを利用することでそれぞれのフィールドに対して HTML 属性を引数として渡すことができます。リスト3-3はクラスに email フィールドに追加する方法を表示します。

リスト3-3 - render() メソッドを利用した HTML 属性のカスタマイズ

<?php echo $form->render(array('email' => array('class' => 'email'))) ?>
 
// 生成される HTML
<input type="text" name="contact[email]" value="" id="contact_email" class="email" />

これによってフォームスタイルをカスタマイズできますが、ページ内のフィールドの編成をカスタマイズするために必要なレベルの柔軟性は提供されません。

表示のカスタマイズ

render() メソッドによって可能なグローバルなカスタマイズを越えて、柔軟性を得るためにそれぞれのフィールドの表示を分解する方法をみてみましょう。

フィールド上で renderRow() メソッドを利用する

これを行う最初の方法はすべてのフィールドを個別に生成することです。実際、リスト3-4で示されるように、<?php echo $form ?> ステートメントはフォーム上で renderRow() メソッドを4回呼び出すことと同等です。

リスト3-4 - renderRow() の使い方

<form action="<?php echo url_for('contact/index') ?>" method="POST">
  <table>
    <?php echo $form['name']->renderRow() ?>
    <?php echo $form['email']->renderRow() ?>
    <?php echo $form['subject']->renderRow() ?>
    <?php echo $form['message']->renderRow() ?>
    <tr>
      <td colspan="2">
        <input type="submit" />
      </td>
    </tr>
  </table>
</form>

form オブジェクトを PHP 配列として利用してそれぞれのフィールドにアクセスします。email フィールドは $form['email'] を通じてアクセスできます。renderRow() メソッドはフィールドを HTML テーブルの列として表示します。$form['email']->renderRow() の表記は email フィールドの列を生成します。subjectemailmessage のほかの3つのフィールドに対して同じ種類のコードを繰り返すことで、フォームの表示を完成させます。

使い始めたこの現在のテンプレートとオリジナルのテンプレートは機能の面では理想的です。 しかしながら、表示が同じ場合、カスタマイズはよりかんたんです。renderRow() メソッドは2つの引数: HTML 属性の配列とラベル名をとります。リスト3-5のコードはフォームをカスタマイズするためにこれら2つの引数を使います (図3-4はレンダリングの結果を表示します)。

リスト3-5 - 表示をカスタマイズするために renderRow() メソッドの引数を使う

<form action="<?php echo url_for('contact/index') ?>" method="POST">
  <table>
    <?php echo $form['name']->renderRow() ?>
    <?php echo $form['email']->renderRow(array('class' => 'email')) ?>
    <?php echo $form['subject']->renderRow() ?>
    <?php echo $form['message']->renderRow(array(), 'Your message') ?>
    <tr>
      <td colspan="2">
        <input type="submit" />
      </td>
    </tr>
  </table>
</form>

図3-4 - renderRow() メソッドを利用したフォーム表示のカスタマイズ

renderRow() メソッドを利用したフォーム表示のカスタマイズ

email フィールドを生成するために renderRow() に送り出される引数をじっくり見てみましょう。

これは message フィールドメッセージで同じように機能します。

すべての renderRow() メソッドの引数はオプションで、namesubject フィールドに対して行ったようなことは必要としません。

renderRow() メソッドがそれぞれのフィールド要素のカスタマイズの手助けになりますが、図3-5で示されるように、レンダリングはこれらの要素を装飾する HTML コードによって制限されます。

図3-5 - renderRow()render() メソッドによって使われる HTML の構造

renderRow() と render() メソッドによって使われる HTML の構造

この構造から自由になるために、図3-6で示されるように、それぞれのフィールドは要素を生成するメソッドをもっています。

図3-6 - フィールドをカスタマイズするために利用できるメソッド

フィールドをカスタマイズするために利用できるメソッド

これらのメソッドはこの章の終わりのほうで説明されます。

フィールド上で render() メソッドを利用する

2つのカラムを持つフォームを表示することを考えてみましょう。図3-7で示されるように、subjectmessage フィールドがそれら独自の列をあらわすとき、nameemail フィールドはそれら独自の列をあらわします。

図3-7 - さまざまな列でフォームを表示する

さまざまな列でフォームを表示する

これを行うにはフィールドのそれぞれの要素を個別に生成できるようにしなければなりません。フィールドにアクセスするために、form オブジェクトを連想配列として使い、フィールド名をキーとして使うことができることをすでに見ました。 たとえば、email フィールドは $form['email'] でアクセスできます。リスト3-6は2つの列をもつフォームを実装する方法を示しています。

リスト3-6 - 2つのカラムで表示方法をカスタマイズする

<form action="<?php echo url_for('contact/index') ?>" method="POST">
  <table>
    <tr>
      <th>Name:</th>
      <td><?php echo $form['name']->render() ?></td>
      <th>Email:</th>
      <td><?php echo $form['email']->render() ?></td>
    </tr>
    <tr>
      <th>Subject:</th>
      <td colspan="3"><?php echo $form['subject']->render() ?></td>
    </tr>
    <tr>
      <th>Message:</th>
      <td colspan="3"><?php echo $form['message']->render() ?></td>
    </tr>
    <tr>
      <td colspan="4">
        <input type="submit" />
      </td>
    </tr>
  </table>
</form>

<?php echo $form ?> ステートメントを使う際に必須ではないフィールド上で render() メソッドを明示的に使うこととまったく同じように、リスト3-7のようにテンプレートを書き換えることができます。

リスト3-7 - 2つのカラムのカスタマイズ作業を簡略化する

<form action="<?php echo url_for('contact/index') ?>" method="POST">
  <table>
    <tr>
      <th>Name:</th>
      <td><?php echo $form['name'] ?></td>
      <th>Email:</th>
      <td><?php echo $form['email'] ?></td>
    </tr>
    <tr>
      <th>Subject:</th>
      <td colspan="3"><?php echo $form['subject'] ?></td>
    </tr>
    <tr>
      <th>Message:</th>
      <td colspan="3"><?php echo $form['message'] ?></td>
    </tr>
    <tr>
      <td colspan="4">
        <input type="submit" />
      </td>
    </tr>
  </table>
</form>

フォームのように、それぞれのフィールドは render() メソッドに HTML 属性の配列を渡すことでカスタマイズできます。リスト3-8は email フィールドの HTML 属性を修正する方法を示しています。

リスト3-8 - render() メソッドを使う HTML 属性を修正する

<?php echo $form['email']->render(array('class' => 'email')) ?>
 
// 生成される HTML
<input type="text" name="contact[email]" class="email" id="contact_email" />

フィールド上で renderLabel() メソッドを利用する

以前のパラグラフでカスタマイズしているあいだラベルを生成しませんでした。リスト3-9はそれぞれのフィールドに対応するラベルを生成するために renderLabel() メソッドを使います。

リスト3-9 - renderLabel() メソッドを使う

<form action="<?php echo url_for('contact/index') ?>" method="POST">
  <table>
    <tr>
      <th><?php echo $form['name']->renderLabel() ?>:</th>
      <td><?php echo $form['name'] ?></td>
      <th><?php echo $form['email']->renderLabel() ?>:</th>
      <td><?php echo $form['email'] ?></td>
    </tr>
    <tr>
      <th><?php echo $form['subject']->renderLabel() ?>:</th>
      <td colspan="3"><?php echo $form['subject'] ?></td>
    </tr>
    <tr>
      <th><?php echo $form['message']->renderLabel() ?>:</th>
      <td colspan="3"><?php echo $form['message'] ?></td>
    </tr>
    <tr>
      <td colspan="4">
        <input type="submit" />
      </td>
    </tr>
  </table>
</form>

ラベルの名前はフィールドの名前から自動的に生成されます。リスト3-10で示されるように renderLabel() メソッドに引数を渡すことでカスタマイズできます。

リスト3-10 - ラベルの名前を修正する

<?php echo $form['message']->renderLabel('Your message') ?>
 
// 生成される HTML
<label for="contact_message">Your message</label>

ラベルの名前を引数として送信する場合 renderLabel() メソッドには何の意味があるのでしょうか?なぜシンプルな HTML の label タグを使わないのでしょうか?これは renderLabel() メソッドは label タグを生成しリンクつきフィールド (id) の識別子にセットされる for 属性を自動的に追加するからです。このことは フィールドがアクセス可能であることを保証します。ラベルをクリックするとき、フィールドは自動的に 焦点を合わせます。

<label for="contact_email">Email</label>
<input type="text" name="contact[email]" id="contact_email" />

さらに、renderLabel() メソッドに第2引数を渡すことで HTML 属性を追加できます。

<?php echo $form['send_notification']->renderLabel(null, array('class' => 'inline')) ?>
 
// 生成される HTML
<label for="contact_send_notification" class="inline">Send notification</label>

この例では、最初の引数は null なのでラベルテキストの自動生成は保存されます。

フィールド上で renderError() メソッドを利用する

現在のテンプレートはエラーメッセージを処理しません。リスト3-11は renderError() メソッドを利用してそれらのメッセージを復元しています。

リスト3-11 - renderError() メソッドを利用してエラーメッセージを表示する

<form action="<?php echo url_for('contact/index') ?>" method="POST">
  <table>
    <tr>
      <th><?php echo $form['name']->renderLabel() ?>:</th>
      <td>
        <?php echo $form['name']->renderError() ?>
        <?php echo $form['name'] ?>
      </td>
      <th><?php echo $form['email']->renderLabel() ?>:</th>
      <td>
        <?php echo $form['email']->renderError() ?>
        <?php echo $form['email'] ?>
      </td>
    </tr>
    <tr>
      <th><?php echo $form['subject']->renderLabel() ?>:</th>
      <td colspan="3">
        <?php echo $form['subject']->renderError() ?>
        <?php echo $form['subject'] ?>
      </td>
    </tr>
    <tr>
      <th><?php echo $form['message']->renderLabel() ?>:</th>
      <td colspan="3">
        <?php echo $form['message']->renderError() ?>
        <?php echo $form['message'] ?>
      </td>
    </tr>
    <tr>
      <td colspan="4">
        <input type="submit" />
      </td>
    </tr>
  </table>
</form>

エラーメッセージのきめ細かいカスタマイズ

renderError() メソッドはフィールドに関連するエラーのリストを生成します。フィールドに何かのエラーが発生する場合、このメソッドは HTML コードを生成します。デフォルトでは、リストは並べ替えられていない HTML リスト (<ul>) として生成されます。

これは一般的なほとんどの問題に適していますが、hasError()getError() メソッドを使うことによってエラーに直接アクセスできます。リスト3-12は email フィールドのエラーメッセージをカスタマイズする方法を示しています。

リスト3-12 - エラーメッセージにアクセスする

<?php if ($form['email']->hasError()): ?>
  <ul class="error_list">
    <?php foreach ($form['email']->getError() as $error): ?>
      <li><?php echo $error ?></li>
    <?php endforeach; ?>
  </ul>
<?php endif; ?>

この例では、生成されたコードは renderError() メソッドによって生成されたコードとまったく同じです。

隠しフィールドを扱う

referrer 隠しフィールドがフォームの必須フィールドとして存在している場合を想定してみましょう。フォームにアクセスするときにこのフィールドはユーザーページのリファラを保存します。リスト3-13で示されるように、最後の可視フィールドが生成されるときに <?php echo $form ?> ステートメントは隠しフィールドの HTML コードを生成し追加します。

リスト3-13 - 隠しフィールドのコードを生成する

<tr>
  <th><label for="contact_message">Message</label></th>
  <td>
    <textarea rows="4" cols="30" name="contact[message]" id="contact_message"></textarea>
    <input type="hidden" name="contact[referrer]" id="contact_referrer" />
  </td>
</tr>

referrer 隠しフィールドに対して生成されたコードでお気づきのように、タグ要素だけが出力に追加されました。ラベルを生成しないために役立ちます。このフィールドで起きる可能性のある潜在的なエラーはどうでしょうか?隠しフィールドであったとしても、故意にもしくはコード内にエラーが存在するため、このフィールドが処理のあいだに汚染される可能性があります。これらのエラーは referrer フィールドに直接接続されていませんが、グローバルエラーに集約されます。5章でグローバルエラーの概念がほかの問題にも拡張されることを見ることになります。図3-8は referrer フィールド上でエラーが発生したときにエラーメッセージが表示される方法を示し、リスト3-14はこれらのエラーに対して生成されたコードを示しています。

renderHiddenFields() メソッドを使うことで、すべての隠しフィールド (CSRF のものも含む) を一度にレンダリングできます。

図3-8 - グローバルエラーのメッセージを表示する

グローバルエラーのメッセージを表示する

リスト3-14 - グローバルエラーのメッセージを生成する

<tr>
  <td colspan="2">
    <ul class="error_list">
      <li>Referrer: Required.</li>
    </ul>
  </td>
</tr>

フォームをカスタマイズするとき、隠しフィールドとグローバルエラーメッセージを実装することをお忘れなく (フォームに対して有効にした保護機能がある場合は CSRF のものをお忘れなく)。

グローバルエラーを扱う

フォームに対して3種類のエラーが存在します。

フィールドに関連するエラーメッセージの実装方法をすでに調べたので、リスト3-15はグローバルエラーメッセージの実装方法を示します。

リスト3-15 - グローバルエラーメッセージを実装する

<form action="<?php echo url_for('contact/index') ?>" method="POST">
  <table>
    <tr>
      <td colspan="4">
        <?php echo $form->renderGlobalErrors() ?>
      </td>
    </tr>
 
    // ...
  </table>

renderGlobalErrors() メソッドを呼び出すとグローバルエラーのリストを表示します。 リスト3-16で示されるように、hasGlobalErrors()getGlobalErrors() メソッドを利用してグローバルエラーにアクセスすることも可能です。

リスト3-16 - hasGlobalErrors()getGlobalErrors() メソッドによるグローバルエラーのカスタマイズ

<?php if ($form->hasGlobalErrors()): ?>
  <tr>
    <td colspan="4">
      <ul class="error_list">
        <?php foreach ($form->getGlobalErrors() as $name => $error): ?>
          <li><?php echo $name.': '.$error ?></li>
        <?php endforeach; ?>
      </ul>
    </td>
  </tr>
<?php endif; ?>

それぞれのグローバルエラーは名前 (name) とメッセージ (error) をもっています。「本物の」グローバルエラーが存在するときは名前は空ですが、隠しフィールドに対するエラーもしくは表示されないフィールドが存在するとき、name はフィールドのラベル名です。

テンプレートが先述のテンプレート (図3-8) と技術的に同等なものであったとしても、こちらはカスタマイズ可能です。

図3-8 - フィールドメソッドを利用してカスタマイズされたフォーム

フィールドメソッドを利用してカスタマイズされたフォーム

国際化

ラベルとエラーメッセージといった、すべてのフォーム要素は symfony の国際化システムによって自動的に扱われます。Web デザイナはフォームを国際化したい、renderLabel() メソッドでラベルを明示的にオーバーライドするときでも、特別なことを行う必要がないことを意味します。翻訳機能は自動的に考慮されます。国際化機能に関する詳細な情報に関しては、9章をご参照ください。

開発者と交流する

symfony を利用した代表的なフォームの開発の説明についてこの章を終わらせましょう:

最初のサイクルが過ぎたら、ビジネスルールのとテンプレートの両方の両方を同時に実行できます。

テンプレートに影響を与えずに、それゆえデザイナチームに介入せずに、開発チームは次の作業を進めることができます。

同じように、デザイナチームは開発チームに頼らずにエルゴノミックもしくはグラフィックな変更を自由に実行できます。

次の活動はチーム内部の調整を必要とします。

ビジネスルールとフォームの表示で変更が必要な場合にこのルールは意味をなします。この章の最初で始めたように、フォームシステムがきれいにタスクを分割していても、チーム内部のコミュニケーションに優ることはありません。

第4章 - Propel との統合 »
« 第2章 - フォームのバリデーション

Questions & Feedback

If you find a typo or an error, please register and open a ticket.

If you need support or have a technical question, please post to the official user mailing-list.