形状


Web 開発者にとって、HTML フォームの処理は最も一般的かつ困難なタスクの 1 つです。 Symfony はフォームコンポーネントを統合して、フォームの処理を簡単にします。この章では、複雑なフォームを最初から作成し、フォーム ライブラリの重要な機能を学習します。

Symfony の Form コンポーネントは、Symfony プロジェクトの外部で使用できる独立したクラス ライブラリです。詳細については、フォーム コンポーネントのドキュメントを参照してください。

単純なフォームを作成する

いくつかの「タスク」を表示するための単純な To Do リストを作成しているとします。ユーザーがタスクを編集および作成できるようにするフォームを作成する必要があります。その前に、単一タスクのデータを表示および保存できる Task クラスを見てみましょう。

// src/AppBundle/Entity/Task.phpnamespace AppBundle\Entity; class Task{
    protected $task;
    protected $dueDate;     public function getTask()
    {
        return $this->task;
    }     public function setTask($task)
    {
        $this->task = $task;
    }     public function getDueDate()
    {
        return $this->dueDate;
    }     public function setDueDate(\DateTime $dueDate = null)
    {
        $this->dueDate = $dueDate;
    }}

これは、Symfony と対話したり、他のライブラリを参照したりしないため、ネイティブ PHP オブジェクト クラスです。これは、your プログラムの task (タスク) のデータ問題を直接解決する、非常に単純な PHP オブジェクト クラスです。もちろん、この章が終わるまでに、HTML フォーム経由で Task インスタンスにデータを送信し、その値を検証し、データベースに保存できるようになります。

フォームの構築

Task クラスを作成したので、次のステップは実際のタスクを作成してレンダリングすることです。 html形式。 Symfony では、これはフォーム オブジェクトを構築し、それをテンプレートにレンダリングすることによって行われます。これで、これらすべてをコントローラーで実行できるようになります。

// src/AppBundle/Controller/DefaultController.phpnamespace AppBundle\Controller; use AppBundle\Entity\Task;use Symfony\Bundle\FrameworkBundle\Controller\Controller;use Symfony\Component\HttpFoundation\Request;use Symfony\Component\Form\Extension\Core\Type\TextType;use Symfony\Component\Form\Extension\Core\Type\DateType;use Symfony\Component\Form\Extension\Core\Type\SubmitType; class DefaultController extends Controller{
    public function newAction(Request $request)
    {
        // create a task and give it some dummy data for this example
        // 创建一个task对象,赋一些例程中的假数据给它
        $task = new Task();
        $task->setTask('Write a blog post');
        $task->setDueDate(new \DateTime('tomorrow'));         $form = $this->createFormBuilder($task)
            ->add('task', TextType::class)
            ->add('dueDate', DateType::class)
            ->add('save', SubmitType::class, array('label' => 'Create Task'))
            ->getForm();         return $this->render('default/new.html.twig', array(
            'form' => $form->createView(),
        ));
    }}

この例では、コントローラーでフォームを直接構築する方法を示します。次の Create form class では、独立したクラスを使用してフォームを構築します。フォームを再利用できるため、この方法をお勧めします。


Symfony のフォームオブジェクトは「フォームビルダー」を通じて作成されるため、フォームの作成には多くのコードは必要ありません。フォーム ビルダーの目的は、単純なフォーム作成の「命令」を記述できるようにすることであり、実際にフォームを作成するときのすべての「オーバーロード」タスクはビルダーによって完了されます。

この例では、フォームに taskdueDate という 2 つのフィールドを追加しました。 Task クラスの task 属性と dueDate 属性に対応します。それぞれに FQCN (完全修飾クラス名/完全パス クラス名) の「タイプ」(TextTypeDateType など) を指定しました。タイプによって、どちらが生成されるかが決まります。フィールドの HTML フォーム タグ (タグ グループ)。

最後に、フォームをサーバーに送信するためのカスタム ラベルを持つ送信ボタンを追加します。

Symfony には多数の組み込み型が付属していますが、これらについて簡単に紹介します (以下の 組み込みフォーム型 を参照)。

フォームのレンダリング

フォームが作成されたら、次のステップはフォームをレンダリングすることです。これは、特定のフォーム「ビュー」オブジェクト (上記のサンプル コントローラーの $form->createView() メソッドに注意してください) をテンプレートに渡し、一連のフォーム ヘルパー関数 ( helper関数)。

TWIG:{# app/Resources/views/default/new.html.twig #}
{{ form_start(form) }}
{{ form_widget(form) }}
{{ form_end(form) }}
PHP:<!-- app/Resources/views/default/new.html.php -->
<?php echo $view['form']->start($form) ?>
<?php echo $view['form']->widget($form) ?>
<?php echo $view['form']->end($form) ?>

1465202253_36066_5344_form-simple.png

この例では、「POST」リクエストでフォームを送信し、それを「フォーム表示 (ページ)」と同じ URL に送信すると想定しています。 )」。後で、フォーム送信後にリクエスト メソッドとターゲット URL を変更する方法を学びます。 ############それでおしまい!完全なフォームをレンダリングするには 3 行だけが必要です:

form_start(form)

  • ファイルのアップロードを使用する場合を含め、フォームの開始タグをレンダリングします。正しい enctype 属性。
  • form_widget(form)
  • フィールド要素自体、フィールドラベル、フィールド検証のためのエラー情報を含むすべてのフィールドをレンダリングします。
  • form_end(form)
  • 各フィールドを手動で生成すると、フォーム終了タグだけでなく、フォーム内のまだレンダリングされていないすべてのフィールドもレンダリングできます。レンダリングされました。これは、非表示フィールドをレンダリングし、自動化されたフィールドを利用する場合に便利です。 CSRF 保護
  • 保護メカニズムを使用する場合に非常に便利です。
  • とてもシンプルですが、(今のところ)あまり柔軟性がありません。通常、フォームのスタイルを制御するには、フォーム内の各フィールドを個別にレンダリングする必要があります。この方法は、後の
フォーム レンダリングを制御する方法
の記事でマスターします。

続行する前に、レンダリングされた task 入力ボックスに $task オブジェクトのプロパティ値 (つまり、「ブログ投稿を書く」) が含まれている理由に注意してください。これはフォームの最初のタスクです。オブジェクトからデータを取得し、それを HTML フォームで表示できるように適切な形式に変換します。

デフォルトでは、フォームは POST リクエストを「レンダリングしたのと同じコントローラー」に送信します。

ここで、フォームの 2 番目のタスクは、ユーザーが送信したデータをオブジェクトのプロパティに転送することです。これを行うには、ユーザーが送信したデータをフォーム オブジェクトに書き込む必要があります。次の機能をコントローラーに追加します。
// ...use Symfony\Component\HttpFoundation\Request; public function newAction(Request $request){
    // just setup a fresh $task object (remove the dummy data)
    // 直接设置一个全新$task对象(删除了假数据)
    $task = new Task();     $form = $this->createFormBuilder($task)
        ->add('task', TextType::class)
        ->add('dueDate', DateType::class)
        ->add('save', SubmitType::class, array('label' => 'Create Task'))
        ->getForm();     $form->handleRequest($request);     if ($form->isSubmitted() && $form->isValid()) {         // $form->getData() holds the submitted values
        // but, the original `$task` variable has also been updated
        //  $form->getData() 持有提交过来的值
        // 但是,原始的 `$task` 变量也已被更新了
        $task = $form->getData();         // ... perform some action, such as saving the task to the database
        // for example, if Task is a Doctrine entity, save it!
        // 一些操作,比如把任务存到数据库中
        // 例如,如果Tast对象是一个Doctrine entity,存下它!
        // $em = $this->getDoctrine()->getManager();
        // $em->persist($task);
        // $em->flush();         return $this->redirectToRoute('task_success');
    }     return $this->render('default/new.html.twig', array(
        'form' => $form->createView(),
    ));}

createView()

メソッドは

handleRequest
# #で呼び出される必要があることに注意してください。

#後もう一度電話してください。そうしないと、*_SUBMIT フォーム イベントへの変更はビュー レイヤーに適用されません (検証中のエラー メッセージなど)。

コントローラーはフォームを処理するときに一般的なパターンに従います。これには 3 つの方法があります:

  1. ブラウザが最初にページを読み込むとき、フォームが作成され、レンダリングされます。 handleRequest() フォームが送信されていないことを認識し、何も行いません。フォームが送信されない場合、isSubmitted() は false を返します。

  2. ユーザーがフォームを送信すると、 handleRequest( ) はこのアクションを認識し、送信されたデータを $task オブジェクトの task プロパティと dueDate プロパティに直ちに書き込みます。その後、オブジェクトが検証されます。無効な場合 (検証については次の章で説明します)、isValid()false を返し、フォームは再度レンダリングされますが、今回のみ検証エラー;

  3. ユーザーが法的データを含むフォームを送信すると、送信されたデータは再度フォームに書き込まれますが、今回は isValid() true を返します。ユーザーを他のページ (「ありがとう」ページや「成功」ページなど) にリダイレクトする前に、$task オブジェクトを使用していくつかの操作 (データベースへの永続化など) を実行する機会があります。 )。

    フォームが正常に送信された後にユーザーをリダイレクトするのは、ユーザーがブラウザーの「更新」ボタンを使用してデータを繰り返し送信するのを防ぐためです。

フォームがいつ送信されるか、またはどのデータがフォームに渡されるかを正確に制御する必要がある場合は、submit() を使用できます。詳細については、「Form::submit() を手動で呼び出す」を参照してください。

フォームの検証

前のセクションでは、有効または無効なデータを含むフォームがどのように送信されるかを学びました。 Symfony では、検証プロセスは基礎となるオブジェクト (Task など) で実行されます。言い換えれば、問題は「フォーム」が有効かどうかではなく、「送信されたデータがフォームに適用された」後に $task オブジェクトが有効かどうかです。 $form->isvalid() の呼び出しは、基礎となる $task オブジェクトに有効なデータを取得したかどうかを問い合わせるショートカットです。

検証は、一連のルール (「制約/制約」と呼ばれる) をクラスに追加することによって実行されます。ルールと制約を Task クラスに追加して、タスク属性を空にすることはできず、duDate フィールドも空ではなく、有効な DateTime オブジェクトでなければならないようにします。

Annotations:// src/AppBundle/Entity/Task.phpnamespace AppBundle\Entity; use Symfony\Component\Validator\Constraints as Assert; class Task{
    /**
     * @Assert\NotBlank()
     */
    public $task;     /**
     * @Assert\NotBlank()
     * @Assert\Type("\DateTime")
     */
    protected $dueDate;}
YAML:# src/AppBundle/Resources/config/validation.ymlAppBundle\Entity\Task:
    properties:
        task:
            - NotBlank: ~
        dueDate:
            - NotBlank: ~
            - Type: \DateTime
rreerree

以上です!ここで無効なデータを使用してフォームを再送信すると、適切なエラーがフォームに出力されるのが表示されます。

検証は Symfony の非常に強力な機能であり、独自の 専用の章があります。

html5 検証

HTML5 以降、多くのブラウザはクライアント側の検証制約をネイティブにサポートしています。最も一般的に使用される検証アクティブ化方法は、必須フィールドに required 属性をレンダリングすることです (翻訳: ドキュメント内の「レンダリング」という単語は英語のレンダリングに相当し、「出力」として理解できます。Symfony では) 、プログラムまたはコントロールの最下層からビュー層までコンテンツを表示するプロセスはレンダリングと呼ばれます)。 HTML5 をサポートするブラウザの場合、ユーザーが空のフィールドをフォームに送信しようとすると、ブラウザ固有のメッセージが表示されます。

生成されたフォームは、検証をトリガーするいくつかの意味のある HTML 属性を追加することにより、この新機能を最大限に活用します。クライアント側の検証は、novalidate 属性を form タグに追加するか、formnovalidate を submit タグに追加することによってオフにすることもできます。これは、サーバー側の検証制約をテストしたいが、空のフィールドを送信するときなど、ブラウザーによってブロックされる場合に便利です。

XML:<!-- src/AppBundle/Resources/config/validation.xml --><?xml version="1.0" encoding="UTF-8"?><constraint-mapping xmlns="http://symfony.com/schema/dic/constraint-mapping"    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"    xsi:schemaLocation="http://symfony.com/schema/dic/constraint-mapping        http://symfony.com/schema/dic/constraint-mapping/constraint-mapping-1.0.xsd">     <class name="AppBundle\Entity\Task">
        <property name="task">
            <constraint name="NotBlank" />
        </property>
        <property name="dueDate">
            <constraint name="NotBlank" />
            <constraint name="Type">\DateTime</constraint>
        </property>
    </class></constraint-mapping>


PHP:// src/AppBundle/Entity/Task.phpuse Symfony\Component\Validator\Mapping\ClassMetadata;use Symfony\Component\Validator\Constraints\NotBlank;use Symfony\Component\Validator\Constraints\Type; class Task{
    // ...     public static function loadValidatorMetadata(ClassMetadata $metadata)
    {
        $metadata->addPropertyConstraint('task', new NotBlank());         $metadata->addPropertyConstraint('dueDate', new NotBlank());
        $metadata->addPropertyConstraint(
            'dueDate',
            new Type('\DateTime')
        );
    }}

組み込みフィールドタイプ

Symfony の標準バージョンには、遭遇する可能性のあるすべての従来のフォームフィールドとデータタイプをカバーする膨大な数のフィールドタイプが含まれています。

テキスト タイプ フィールド

ChoiceType

ファイルタイプ

繰り返しタイプ

隠しフィールド

##FormType

フィールド タイプのオプション

各フィールド タイプには、構成のための特定の数のオプションがあります。たとえば、

dueDate フィールドは現在 3 つの選択ボックスとして表示されます。 DateType

日付フィールドは、単一のテキスト ボックスとして表示されるように構成できます (ユーザーは文字列を日付として入力できます)。

PHP:<!-- app/Resources/views/default/new.html.php --><?php echo $view['form']->form($form, array(
    'attr' => array('novalidate' => 'novalidate'),)) ?>
#
Twig:{# app/Resources/views/default/new.html.twig #}
{{ form(form, {'attr': {'novalidate': 'novalidate'}}) }}
#

-simple2.png

#各フィールド タイプには、タイプを渡すための異なるオプション セットがあります。フィールド タイプの詳細については、各タイプのドキュメントを参照してください。

必須オプション

最も一般的に使用されるオプションは、任意のフィールドに適用できる required オプションです。デフォルトでは、 true に設定されています。これは、HTML5 をサポートするブラウザは、クライアント側の検証を使用してフィールドが空かどうかを判断することを意味します。この動作を望まない場合は、HTML5 検証をオフにする か、フィールドの required オプションを false に設定します。

1

requiredtrue および not に設定すると、サーバー側の検証が使用されることになることに注意してください。言い換えると、ユーザーがこのフィールドに空白の値を送信した場合 (古いブラウザーや Web サービスの使用時など)、Symfony の NotBlank を使用していない限り、空白の値は有効な値として受け入れられます。 または NotNull 検証制約。

言い換えると、required オプションは「便利」ですが、サーバー側の検証は 常に使用する必要があります。

label オプション

フォーム フィールドでは、

label オプションを使用してラベルを設定できます。フォーム フィールド、任意のフィールドに適用されます。

->add('dueDate', DateType::class, array('widget' => 'single_text'))

フィールドのラベルは、テンプレートがフォームをレンダリングするときに設定することもできます。以下を参照してください。入力にラベルを関連付ける必要がない場合は、オプション値を

false に設定できます。

フィールド タイプの推測

これで、検証メタデータ (アノテーション: アノテーション) が

タスク に追加されました。皆さん、Symfony はすでにあなたのフィールドについて何かを知っています。これを許可すると、Symfony はフィールドのタイプを「推測」して設定できます。次の例では、Symfony は検証ルールに基づいて、task フィールドが標準の TextType フィールドであり、dueDateDateType## であると推測できます。 # 分野 。

->add('dueDate', DateType::class, array(
    'widget' => 'single_text',
    'required' => false))
「Guess」は、

add()

メソッドの 2 番目のパラメータを省略した場合 (または null を入力した場合) に有効になります。 3 番目の引数としてオプションの配列 (上記の dueDate など) を入力すると、それらのオプションは推測されるフィールドに適用されます。

フォームで特定の検証グループを使用している場合でも、フィールド タイプを推測すると、
すべての

検証制約 (この制約に属さない制約も含む) が考慮されます。 「使用中」検証グループ)。

フィールドタイプのオプションを推測する

フィールドタイプを推測することに加えて、Symfony はフィールドオプションの正しい値を推測することもできます。

これらのオプションを設定すると、HTML5 クライアント側検証で使用するための特別な HTML 属性を使用してフィールドがレンダリングされます。ただし、サーバー側では対応する検証ルール (Assert\Length など) は生成されません。これらのサーバー側ルールを手動で追加する必要がありますが、これらのフィールド タイプのオプションはこれらのルールに基づいて推測できます。

  • 必須
  • 必須 オプションは検証ルールに基づいて指定できます (例: フィールド ## です) # NotBlank または NotNull) または Doctrine メタデータのメタデータ (たとえば、フィールドが nullable かどうか) が推測されます。クライアント側の検証が自動的に検証ルールと一致するため、これは非常に便利です。
  • max_length
  • フィールドが列テキスト フィールドの場合、
  • max_length オプションは検証制約 (フィールドに # があるかどうか) に基づいて指定できます。 ## 適用される Length または Range) または Doctrine メタデータ (フィールドの長さによる)。
これらのフィールドオプション
は、Symfony を型推測に使用する場合 (つまり、引数を省略するか、

null# を渡す場合) にのみ使用されます。 ## as add() メソッドの 2 番目のパラメーター) が推測されます。 推測された (オプション) 値を変更する場合は、この項目をフィールド タイプのオプション配列に渡してオーバーライドできます。

->add('dueDate', DateType::class, array(
    'widget' => 'single_text',
    'label'  => 'Due Date',))
#
public function newAction(){
    $task = new Task();     $form = $this->createFormBuilder($task)
        ->add('task')
        ->add('dueDate', null, array('widget' => 'single_text'))
        ->add('save', SubmitType::class)
        ->getForm();}
#

フォーム クラスの作成

ご覧のとおり、フォームはコントローラーで直接作成して使用できます。ただし、より良いアプローチは、別の PHP クラスでフォームを作成することです。プログラム内のどこでも再利用できます。 「タスク フォームの作成」に必要なロジックを保持する新しいクラスを作成します。

1

この新しいクラスには、タスク フォームの作成に必要なすべての要素が含まれています。これを使用すると、コントローラーでフォームをすばやく作成できます。

->add('task', null, array('attr' => array('maxlength' => 4)))

フォーム ロジックを独自のクラスに配置すると、プロジェクト内のどこでもフォームを簡単に再利用できるようになります。これはフォームを作成する最良の方法ですが、決定はあなた次第です。

Set data_class

すべてのフォームは、「基礎となるデータを保持するクラス」の名前 (# など) を知っている必要があります。 ## AppBundle\Entity\Task )。通常、これは createForm メソッドに渡される 2 番目の引数 (例: $task ) に基づいて推測されます。後でフォームの埋め込みを開始すると、これでは十分ではなくなります。したがって、厳密に必要というわけではありませんが、次のコードをフォーム タイプ クラスに追加して、data_class オプションを明示的に指定することをお勧めします。

// src/AppBundle/Form/Type/TaskType.phpnamespace AppBundle\Form\Type; use Symfony\Component\Form\AbstractType;use Symfony\Component\Form\FormBuilderInterface;use Symfony\Component\Form\Extension\Core\Type\SubmitType; class TaskType extends AbstractType{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('task')
            ->add('dueDate', null, array('widget' => 'single_text'))
            ->add('save', SubmitType::class)
        ;
    }}

フォームをオブジェクトにマッピングすると、すべてのフィールドがマッピングされます。マップされたオブジェクトに「存在しない」フォーム内のフィールドは例外をスローします。

フォーム内で追加フィールド (「これらの記述に同意しますか?」チェックボックスなど) を使用する必要があり、このフィールドが基になるフィールドにマップされない場合は、

を設定する必要があります。マップされたオプションは false:

// src/AppBundle/Controller/DefaultController.phpuse AppBundle\Form\Type\TaskType; public function newAction(){
    $task = ...;
    $form = $this->createForm(TaskType::class, $task);     // ...}

さらに、フォームのフィールドが送信されたデータに含まれていない場合、これらのフィールドは明示的に # に設定されます。 ##ヌル ###。

コントローラーでは、フィールド データ (フィールド値) にアクセスできます:

use Symfony\Component\OptionsResolver\OptionsResolver; public function configureOptions(OptionsResolver $resolver){
    $resolver->setDefaults(array(
        'data_class' => 'AppBundle\Entity\Task',
    ));}
##さらに、マップされていないフィールドのデータを直接変更することもできます:
use Symfony\Component\Form\FormBuilderInterface; public function buildForm(FormBuilderInterface $builder, array $options){
    $builder
        ->add('task')
        ->add('dueDate', null, array('mapped' => false))
        ->add('save', SubmitType::class)
    ;}

1
$form->get('dueDate')->getData();

最終的な考え方

フォームを作成するときは、主な目標はオブジェクト (##) を配置することであることに留意してください。 #task ) データを HTML フォームに変換して、ユーザーが値を変更 (フォーム) できるようにします。 2 番目の目標は、ユーザーが送信したデータを取得し、オブジェクトに対して再動作することです。

まだまだ習得すべきことはたくさんありますが、フォーム システムには

強力な高度なテクニックがたくさんあります。