あっぽログ
← 記事一覧に戻る

SymfonyのFormコンポーネント入門:フォーム作成・バリデーション・レンダリングを使いこなす

はじめに

Webアプリケーション開発において、フォームの処理は避けて通れません。入力値の取得・バリデーション・エラーメッセージの表示など、自前で実装すると意外と手間がかかります。

SymfonyのFormコンポーネントはこれらをまとめて解決してくれる強力な仕組みです。この記事では、フォームクラスの作成からバリデーション・テンプレートへのレンダリングまでを順を追って解説します。

Formコンポーネントのインストール

Symfonyプロジェクトがまだない場合は以下で作成します。

composer create-project symfony/skeleton my-project
cd my-project

Formコンポーネントと、バリデーションに必要なパッケージを追加します。

composer require symfony/form symfony/validator symfony/twig-bundle

フォームクラスを作る

Symfonyではフォームの定義を専用クラス(FormType)として切り出すのが推奨スタイルです。
例として「お問い合わせフォーム」を作ってみましょう。

// src/Form/ContactType.php
<?php

namespace App\Form;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\EmailType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Validator\Constraints as Assert;

class ContactType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options): void
    {
        $builder
            ->add('name', TextType::class, [
                'label' => 'お名前',
                'constraints' => [
                    new Assert\NotBlank(['message' => '名前を入力してください']),
                    new Assert\Length(['max' => 50]),
                ],
            ])
            ->add('email', EmailType::class, [
                'label' => 'メールアドレス',
                'constraints' => [
                    new Assert\NotBlank(['message' => 'メールアドレスを入力してください']),
                    new Assert\Email(['message' => 'メールアドレスの形式が正しくありません']),
                ],
            ])
            ->add('message', TextareaType::class, [
                'label' => 'お問い合わせ内容',
                'constraints' => [
                    new Assert\NotBlank(['message' => '内容を入力してください']),
                    new Assert\Length(['min' => 10, 'max' => 1000]),
                ],
            ])
            ->add('submit', SubmitType::class, ['label' => '送信する']);
    }

    public function configureOptions(OptionsResolver $resolver): void
    {
        $resolver->setDefaults([]);
    }
}

AbstractType を継承し、buildForm() メソッドの中にフィールドを追加していきます。各フィールドには constraints オプションでバリデーションルールを設定できます。

コントローラでフォームを処理する

次に、フォームを生成してリクエストを処理するコントローラを実装します。

// src/Controller/ContactController.php
<?php

namespace App\Controller;

use App\Form\ContactType;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;

class ContactController extends AbstractController
{
    #[Route('/contact', name: 'contact')]
    public function index(Request $request): Response
    {
        // フォームオブジェクトを作成
        $form = $this->createForm(ContactType::class);

        // リクエストの内容をフォームに渡す
        $form->handleRequest($request);

        // フォームが送信済み かつ バリデーション通過
        if ($form->isSubmitted() && $form->isValid()) {
            $data = $form->getData();

            // ここでメール送信やDB保存などの処理を行う
            // 例: $data['name'], $data['email'], $data['message']

            $this->addFlash('success', 'お問い合わせを受け付けました。');

            return $this->redirectToRoute('contact');
        }

        return $this->render('contact/index.html.twig', [
            'form' => $form,
        ]);
    }
}

ポイント解説

  • createForm() でフォームオブジェクトを生成
  • handleRequest() でPOSTデータをフォームに自動でバインド
  • isSubmitted()isValid() を組み合わせてバリデーション結果を確認
  • バリデーション失敗時は同じテンプレートを再表示してエラーを見せる

Twigテンプレートでフォームをレンダリングする

Symfonyのフォームはいくつかのヘルパー関数を使って簡単にレンダリングできます。

{# templates/contact/index.html.twig #}
{% extends 'base.html.twig' %}

{% block body %}
    <h1>お問い合わせ</h1>

    {# フラッシュメッセージの表示 #}
    {% for message in app.flashes('success') %}
        <div class="alert alert-success">{{ message }}</div>
    {% endfor %}

    {{ form_start(form) }}
        {{ form_errors(form) }}

        <div>
            {{ form_label(form.name) }}
            {{ form_widget(form.name) }}
            {{ form_errors(form.name) }}
        </div>

        <div>
            {{ form_label(form.email) }}
            {{ form_widget(form.email) }}
            {{ form_errors(form.email) }}
        </div>

        <div>
            {{ form_label(form.message) }}
            {{ form_widget(form.message) }}
            {{ form_errors(form.message) }}
        </div>

        {{ form_widget(form.submit) }}
    {{ form_end(form) }}
{% endblock %}

よく使うTwigのフォーム関数

関数説明
form_start(form)<form> タグを出力(methodやactionを自動設定)
form_end(form)</form> タグを出力(未レンダリングフィールドも出力)
form_label(field)フィールドのラベルを出力
form_widget(field)フィールドの入力欄を出力
form_errors(field)バリデーションエラーを出力
form_row(field)label・widget・errorsをまとめて出力

一気に書きたい場合は form_row() を使うとシンプルになります。

{{ form_start(form) }}
    {{ form_row(form.name) }}
    {{ form_row(form.email) }}
    {{ form_row(form.message) }}
    {{ form_widget(form.submit) }}
{{ form_end(form) }}

エンティティとフォームを連携する

実際のアプリケーションでは、フォームデータをエンティティ(オブジェクト)に直接バインドすることが多いです。

// src/Entity/Contact.php
<?php

namespace App\Entity;

class Contact
{
    public string $name = '';
    public string $email = '';
    public string $message = '';
}

コントローラ側でエンティティを渡します。

$contact = new Contact();
$form = $this->createForm(ContactType::class, $contact);
$form->handleRequest($request);

if ($form->isSubmitted() && $form->isValid()) {
    // $contact->name, $contact->email などに値が入っている
    dump($contact);
}

createForm() の第2引数にオブジェクトを渡すと、送信後にそのオブジェクトへ自動でマッピングされます。これにより $form->getData() を使わなくてもエンティティから直接値を取り出せます。

まとめ

  • フォームの定義は AbstractType を継承した専用クラスに書く
  • バリデーションは constraints オプションで各フィールドに設定する
  • コントローラでは handleRequest()isSubmitted()isValid() の流れで処理する
  • Twigでは form_start / form_widget / form_errors などのヘルパーを使う
  • エンティティを渡すことでデータのマッピングをシンプルに保てる

SymfonyのFormコンポーネントは最初は覚えることが多く感じますが、一度パターンを掴めば素早くフォームを実装できるようになります。ぜひ実際に手を動かしてみてください。

← 記事一覧に戻る