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