Symfony 5 でビルドした動的フォームにデフォルトの条件付きロジックが必要なユースケースがあります。
私のユースケースとは何か、そして私の問題を簡単なフォームで説明してみましょう。たとえば、次の 2 つのフィールドを持つフォーム Product があります。
- パーツ (choiceType => 左、右)
- 長さ (numberType)
変更すると、すべてのフィールド (:input) が Ajax リクエストを通じて送信されます。ページにアクセスするための2つのコントローラーメソッド(フォームがビルドされている)と、ajaxリクエストを介してフォームをレンダリングするために呼び出されるコントローラーメソッド(条件付きロジックを処理する)があります。
条件付きロジック部分については、次のことを行う必要があります
- パーツを残す場合、デフォルトの長さは 50 にする必要があります
- パーツが正しい場合、デフォルトの長さは 100 にする必要があります
- ユーザーはデフォルトのデータを変更できます
左または右に基づいて長さのデフォルト データを設定することは問題ではありません。左を選択すると、デフォルトの長さは 50 になります。値を 55 に変更すると (変更のたびにフォームが送信されます)、再び 50 になります。この動作は論理的ですが、既定のデータはどのように上書きされたのでしょうか?
上記の状況は、ユーザーにデフォルトのデータを変更するオプションを与えると説明することもできます
フォームタイプ
<?php
// ... namespace, use statments
class ProductType extends AbstractType
{
/**
* {@inheritDoc}
*/
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$builder->add('part', ChoiceType::class, array(
'choices' => array(
'Left' => 'left',
'Right' => 'right',
)
));
$builder->add('length', NumberType::class);
$builder->addEventListener(FormEvents::POST_SET_DATA, function(FormEvent $event) use ($options)
{
$form = $event->getForm();
if(null === $product = $event->getData()) {
return;
}
switch($product->getPart()) {
case 'left': $defaultLength = 50; break;
case 'right': $defaultLength = 100; break;
default: $defaultLength = 0;
}
$form->get('length')->setData($defaultLength);
});
}
/**
* {@inheritDoc}
*/
public function getName(): string
{
return 'product';
}
/**
* {@inheritDoc}
*/
public function configureOptions(OptionsResolver $resolver): void
{
$resolver->setDefaults(array(
'data_class' => Product::class,
'translation_domain' => 'forms',
));
}
}
コントローラ
// src/Controller/ProductController.php
// ... namespace, use statments
namespace App\Controller;
class ProductController extends AbstractController
{
public function productAction(Request $request): Response
{
$product = new Product();
$form = $this->createForm(ProductType::class, $product);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$product = $form->getData();
dd($product);
}
return $this->render('product_view.html.twig', array(
'form' => $form->createView()
));
}
public function productConfigureAjaxAction(Request $request): Response
{
$product = new Product();
$part = $request->request->get('product')['part'] ?? null;
$product->setPart($part);
$form = $this->createForm(ProductType::class, $product);
$form->handleRequest($request);
// product_form.html.twig is an separated file and included in product_view.html.twig
// by making the form separated is could been used for an ajax response
return $this->render('product_form.html.twig', array(
'form' => $form->createView()
));
}
}