Symfonyでネストされたフォーム(CollectionType)を完全ガイド!初心者でもわかるフォーム処理入門
生徒
「Symfonyで親フォームの中に、複数の子フォームを入れたいのですが、どうやって作るのですか?例えば、ひとつの商品に複数のタグを付けるような入力欄です。」
先生
「Symfonyでは、CollectionTypeという仕組みを使うことで、親フォームの中に子フォームをいくつも入れられます。ネストされたフォームと呼ばれる機能ですね。」
生徒
「ネストってどういう意味なんですか?初心者の僕でも理解できますか?」
先生
「ネストとは“入れ子”という意味です。紙のアンケートで『商品の情報』の中に『タグを複数書く欄』があるようなイメージです。Symfonyでも同じように“フォームの中にフォームを入れる”ことができますよ。」
1. ネストされたフォームとは?
Symfonyのフォーム処理では、ひとつのフォームの内部に複数の入力欄のセットをまとめて扱うことができます。この仕組みをネストされたフォームと呼び、実現するための仕組みがCollectionTypeです。
例えば、ひとつの商品データ(親フォーム)の中に「複数のタグ」「複数の写真」「複数の住所」などを入力したい場合、ひとつずつのタグ入力欄(子フォーム)をまとめたCollectionTypeを使います。
紙の書類で例えると、商品の申込用紙(親フォーム)があり、その中に「タグを書き込む欄」が複数セットになっているイメージです。タグ欄は同じ形をしているため、CollectionTypeではそれをまとめて管理してくれます。
Symfony の検索キーワードとしては「Symfony CollectionType ネスト」「Symfony 子フォーム」「Symfony 複数入力」などがよく使われます。
2. CollectionTypeが必要になる場面を理解しよう
Symfonyのフォームを使って開発すると、次のような場面でCollectionTypeが役立ちます。
- 複数のタグ入力欄をまとめて扱いたい
- ユーザーが複数の電話番号を登録できるようにしたい
- 商品に複数の写真を追加したい
- アンケートで複数の選択肢を入力して保存したい
一般的なフォームでは単一の値を扱いますが、SymfonyのCollectionTypeでは「同じ形式の入力欄を繰り返し持てる」ため、柔軟なデータ構造を扱うことができます。
また、CollectionTypeはDoctrine ORMと連携することも多く、エンティティの中に配列やリストで関連するデータを保持しているケースでも便利です。
3. CollectionTypeを使ったフォーム構造の基本
ここでは、商品の中に複数のタグを登録する例を使って説明します。まずは、親エンティティと子エンティティの関係をイメージしましょう。
商品(Product)
- 商品名
- 価格
- タグ(複数)
タグ(Tag)
- タグ名
Symfonyのフォームでは、ProductType(親フォーム)にCollectionTypeを追加し、その中にTagType(子フォーム)を入れます。
$builder->add('tags', CollectionType::class, [
'entry_type' => TagType::class,
'allow_add' => true,
'allow_delete' => true,
]);
entry_typeには「子フォームの型」を指定します。ここではTagTypeが子フォームに相当します。
allow_addは「タグを追加してよい」という許可、allow_deleteは「タグを削除してよい」という許可です。
紙の書類で例えると「追加入力欄を増やしてよい」「不要なら消してよい」というルールを設定しているようなイメージです。
4. 子フォーム(TagType)を作ってみよう
子フォームは、タグひとつ分の入力欄を定義します。Symfonyでは、独立したフォームタイプとして作成します。
$builder->add('name', TextType::class);
このTagTypeを親フォームのCollectionTypeに組み込むことで、親フォームからまとめて扱えるようになります。
子フォームが複数ある場合も同じように、このフォームクラスが繰り返し利用されます。
5. Twigテンプレートでネストされたフォームを描画しよう
フォームがネストされている場合、Twigでは親フォームの中で子フォームが自動的にレンダリングされます。
<div>
{{ form_row(form.name) }}
</div>
<div>
{{ form_row(form.tags) }}
</div>
CollectionTypeの場合、form.tagsの中にタグ入力欄が複数出力されます。
タグが3つあれば、3つ分のTagTypeがレンダリングされ、ユーザーは複数のタグを入力できるようになります。
6. CollectionTypeとDoctrineの連携の仕組み
Symfonyのフォーム処理では、Doctrine ORMと連携することで、ネストされたデータも自然な形でデータベースに保存できます。
親エンティティのProductに対して、Tagは「OneToMany」や「ManyToMany」などの関連を持ちます。この連携によって、フォームで入力されたタグが親エンティティに紐づいて保存されます。
紙の書類でたとえるなら、商品台帳(Product)と、その商品にひもづくタグ一覧(Tag)がセットで保存されるイメージです。
7. ネストされたフォームが動かないときの確認ポイント
7-1. 子フォームの設定が適切か確認する
子フォーム(TagType)に正しいフィールドが設定されていないと、親フォームから値が渡されません。
7-2. Doctrineの関連設定が正しいか確認する
エンティティ間の関連が正しく設定されていないと、データ保存時にエラーが発生することがあります。
7-3. CollectionTypeのオプションを確認する
allow_add、allow_deleteが適切に設定されていなければ、新しい子フォームを追加しても保存されません。
Symfonyにおけるネストされたフォームは、最初は難しく見えるかもしれませんが、紙の台帳と追加入力欄の関係として考えると理解しやすくなります。