LoginSignup
0
0

フォームを操る、"Form"

Last updated at Posted at 2023-12-10

Symfony Component Advent Calendar 2023の11日目の記事です。

フォームを操る、"Form"

Formは名前の通り、フォームを生成したり処理したりするコンポーネントです。

インストール

composer require symfony/form

使い方

  1. Forms::createFormFactory()FormFactoryオブジェクトを作成
  2. createBuilder() でFormBuilderオブジェクトを作成
  3. add()メソッドを使って項目を追加
  4. 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の場合は、AbstractControllerFormFactoryが事前に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) }}

出力例

スクリーンショット 2023-12-07 21.17.04.png

入力タグですが{{ form_widget(form.name) }}のように項目を一つずつ出力することもできます。この場合は入力タグしか出力されず、{{ form_row(form.name) }}を使うと項目ラベルと入力タグを出力します。

FormType

Controllerに直接フォームの内容を書くのは見た目が悪いです。FormTypeクラスを作成すると、そこにフォームの内容を抜き出せます。また、configureOptions()メソッド内で、['data_class' => '{DTOやEntityのクラス名}']を指定すると、getData()で取得する値が指定したクラスのオブジェクトになります。

src/Form/MealFormType.php
<?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 と絡めることで強力なフォーム体験を得ることができるので、おすすめのコンポーネントのひとつです。

※ ちょっとクセがあります。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0