Symfony Component Advent Calendar 2023の11日目の記事です。
フォームを操る、"Form"
Formは名前の通り、フォームを生成したり処理したりするコンポーネントです。
インストール
composer require symfony/form
使い方
-
Forms::createFormFactory()
でFormFactory
オブジェクトを作成 -
createBuilder()
でFormBuilderオブジェクトを作成 -
add()
メソッドを使って項目を追加 -
getForm()
メソッドを使ってForm
オブジェクトを作成
によって、指定したフォームの内容を受け取れるForm
オブジェクトを扱えるようになります。
handleRequest()
を実行すると、実行したフォームメソッドに合わせて$_GET
, $_POST
から値を取得して反映させます。反映した値はgetData()
で取得できます。
use Symfony\Component\Form\Extension\Core\Type\EmailType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\Forms;
$formFactory = Forms::createFormFacgtory();
$form = $formFactory->createNamedBuilder('form')
->add('name', TextType::class)
->add('email', EmailType::class)
->add('submit', SubmitType::class)
->getForm()
;
$form->handleRequest();
$form->getData(); // フォームの入力値 例:['name' => 'すみだ', 'email' => 'test@some.domain']
Symfonyの場合
Symfonyの場合は、AbstractController
にFormFactory
が事前にDIされており、createFormBuilder
メソッドを実行するとFormBuilder
オブジェクトが取得できます。あとは↑と同じですが、handleRequest()
にRequest
オブジェクトを渡すようになります。
class FormController extends AbstractController
{
public function index(Request $request)
{
$form = $this->createFormBuilder()
->add('name', TextType::class)
->add('email', EmailType::class)
->add('submit', SubmitType::class)
->getForm();
$form->handleRequest($request);
return $this->render('form/index.html.twig', [
'form' => $form,
]);
}
}
そして、Twig側ですが{{ form_start(form) }}
, {{ form_end(form) }}
でフォームタグを出力、{{ form_widget(form) }}
で入力タグを出力します。入力された値は自動でフォームに反映されています。
{{ form_start(form) }}
{{ form_widget(form) }}
{{ form_end(form) }}
出力例
入力タグですが{{ form_widget(form.name) }}
のように項目を一つずつ出力することもできます。この場合は入力タグしか出力されず、{{ form_row(form.name) }}
を使うと項目ラベルと入力タグを出力します。
FormType
Controllerに直接フォームの内容を書くのは見た目が悪いです。FormType
クラスを作成すると、そこにフォームの内容を抜き出せます。また、configureOptions()
メソッド内で、['data_class' => '{DTOやEntityのクラス名}']
を指定すると、getData()
で取得する値が指定したクラスのオブジェクトになります。
<?php
namespace App\Form;
use App\Dto\MealDto;
use App\Enum\MealType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\EnumType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class MealFormType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$builder
->add('type', EnumType::class, ['class' => MealType::class])
->add('content', TextType::class)
->add('submit', SubmitType::class)
;
}
public function configureOptions(OptionsResolver $resolver): void
{
$resolver->setDefaults([
'data_class' => MealDto::class
]);
}
}
Controller側ではcreateForm({FormTypeのクラス名})
を呼ぶと指定のForm
オブジェクトを取得できます。
また、createForm('{FormTypeのクラス名}', {オブジェクト})
と、第二引数にフォームと同じクラスのオブジェクトを渡すと、そのオブジェクトに値を代入してくれます。handleRequest($request)
後は、入力した値が代入されています。
代入するためには、Setterメソッドを用意するかプロパティがpublicである必要があります。
public function meal(Request $request): Response
{
$dto = new MealDto(); // この状態では空のオブジェクト
$form = $this->createForm(MealFormType::class, $dto);
$form->handleRequest($request); // ここで、$dtoに入力された値が代入される
return $this->render('form/meal.html.twig', [
'form' => $form,
]);
}
バリデーション
Validatorコンポーネントをインストールすればバリデーションも可能です。バリデーションの記述方法は←のリンク先を見ていただくとして、isValid()
を実行すると、バリデーションが問題ないか判断できます。
public function meal(Request $request): Response
{
$dto = new MealDto();
$form = $this->createForm(MealFormType::class, $dto);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
// 入力が正常だった場合の処理
}
$errors = $form->getErrors(); // バリデーションエラー取得
return $this->render('form/meal.html.twig', [
'form' => $form,
]);
}
Twig側では{{ form_errors(form) }}
でエラー内容が出力されます。form_widget()
同様、項目一つずつ出力することもできます。
{{ form_errors(form) }} フォームのエラー内容すべて
{{ form_errors(form.content }} content項目だけのエラー内容
CSRF対策
Security CSRF
コンポーネントを使うとCSRF対策もできます。
composer require symfony/security-csrf
Symfonyの場合、このモジュールをインストールすれば、自動でCSRF対策されたフォームが出力されます。
<form name="form" method="post">
<div><label for="form_name" class="required">Name</label><input type="text" id="form_name" name="form[name]" required="required"></div>
<div><label for="form_email" class="required">Email</label><input type="email" id="form_email" name="form[email]" required="required"></div>
<div><button type="submit" id="form_submit" name="form[submit]">Submit</button></div>
<!-- ここが自動的に挿入される -->
<input type="hidden" id="form__token" name="form[_token]" value="ad7e7d217c4fa272093.clWlAiEDkjGhsTCi1gZw2M2R6gZizcw_OSANwM4MIc0.CzLwYFdTw0DH-HvyrHQzkp3bkn4tnKNwWGVBtP9ucK4LMvRLZWXBAcTyZQ">
<!-- /ここまで -->
</form>
まとめ
今回はForm
でした。Twig
, Security CSRF
, Validator
と絡めることで強力なフォーム体験を得ることができるので、おすすめのコンポーネントのひとつです。
※ ちょっとクセがあります。