diff --git a/src/Symfony/Component/Form/Extension/Core/Type/FormType.php b/src/Symfony/Component/Form/Extension/Core/Type/FormType.php index 747269494e..7fa7b60fc1 100644 --- a/src/Symfony/Component/Form/Extension/Core/Type/FormType.php +++ b/src/Symfony/Component/Form/Extension/Core/Type/FormType.php @@ -96,6 +96,16 @@ class FormType extends BaseType } $helpTranslationParameters = array_merge($view->parent->vars['help_translation_parameters'], $helpTranslationParameters); + + $rootFormAttrOption = $form->getRoot()->getConfig()->getOption('form_attr'); + if ($options['form_attr'] || $rootFormAttrOption) { + $view->vars['attr']['form'] = \is_string($rootFormAttrOption) ? $rootFormAttrOption : $form->getRoot()->getName(); + if (empty($view->vars['attr']['form'])) { + throw new LogicException('"form_attr" option must be a string identifier on root form when it has no id.'); + } + } + } elseif (\is_string($options['form_attr'])) { + $view->vars['id'] = $options['form_attr']; } $formConfig = $form->getConfig(); @@ -210,6 +220,7 @@ class FormType extends BaseType 'is_empty_callback' => null, 'getter' => null, 'setter' => null, + 'form_attr' => false, ]); $resolver->setAllowedTypes('label_attr', 'array'); @@ -221,6 +232,7 @@ class FormType extends BaseType $resolver->setAllowedTypes('is_empty_callback', ['null', 'callable']); $resolver->setAllowedTypes('getter', ['null', 'callable']); $resolver->setAllowedTypes('setter', ['null', 'callable']); + $resolver->setAllowedTypes('form_attr', ['bool', 'string']); $resolver->setInfo('getter', 'A callable that accepts two arguments (the view data and the current form field) and must return a value.'); $resolver->setInfo('setter', 'A callable that accepts three arguments (a reference to the view data, the submitted value and the current form field).'); diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/Type/FormTypeTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/Type/FormTypeTest.php index b98d32f04b..ffbcfbaa64 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Core/Type/FormTypeTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Core/Type/FormTypeTest.php @@ -14,6 +14,7 @@ namespace Symfony\Component\Form\Tests\Extension\Core\Type; use Symfony\Component\Form\CallbackTransformer; use Symfony\Component\Form\DataMapperInterface; use Symfony\Component\Form\Exception\InvalidArgumentException; +use Symfony\Component\Form\Exception\LogicException; use Symfony\Component\Form\Exception\TransformationFailedException; use Symfony\Component\Form\Extension\Core\Type\CurrencyType; use Symfony\Component\Form\Extension\Core\Type\FormType; @@ -774,6 +775,67 @@ class FormTypeTest extends BaseTypeTest $this->assertSame($error, $form->get('inherit_data_type')->getErrors()[0]); $this->assertCount(0, $form->get('inherit_data_type')->get('child')->getErrors()); } + + public function testFormAttrOnRoot() + { + $view = $this->factory + ->createNamedBuilder('parent', self::TESTED_TYPE, null, [ + 'form_attr' => true, + ]) + ->add('child1', $this->getTestedType()) + ->add('child2', $this->getTestedType()) + ->getForm() + ->createView(); + $this->assertArrayNotHasKey('form', $view->vars['attr']); + $this->assertSame($view->vars['id'], $view['child1']->vars['attr']['form']); + $this->assertSame($view->vars['id'], $view['child2']->vars['attr']['form']); + } + + public function testFormAttrOnChild() + { + $view = $this->factory + ->createNamedBuilder('parent', self::TESTED_TYPE) + ->add('child1', $this->getTestedType(), [ + 'form_attr' => true, + ]) + ->add('child2', $this->getTestedType()) + ->getForm() + ->createView(); + $this->assertArrayNotHasKey('form', $view->vars['attr']); + $this->assertSame($view->vars['id'], $view['child1']->vars['attr']['form']); + $this->assertArrayNotHasKey('form', $view['child2']->vars['attr']); + } + + public function testFormAttrAsBoolWithNoId() + { + $this->expectException(LogicException::class); + $this->expectErrorMessage('form_attr'); + $this->factory + ->createNamedBuilder('', self::TESTED_TYPE, null, [ + 'form_attr' => true, + ]) + ->add('child1', $this->getTestedType()) + ->add('child2', $this->getTestedType()) + ->getForm() + ->createView(); + } + + public function testFormAttrAsStringWithNoId() + { + $stringId = 'custom-identifier'; + $view = $this->factory + ->createNamedBuilder('', self::TESTED_TYPE, null, [ + 'form_attr' => $stringId, + ]) + ->add('child1', $this->getTestedType()) + ->add('child2', $this->getTestedType()) + ->getForm() + ->createView(); + $this->assertArrayNotHasKey('form', $view->vars['attr']); + $this->assertSame($stringId, $view->vars['id']); + $this->assertSame($view->vars['id'], $view['child1']->vars['attr']['form']); + $this->assertSame($view->vars['id'], $view['child2']->vars['attr']['form']); + } } class Money diff --git a/src/Symfony/Component/Form/Tests/Fixtures/Descriptor/resolved_form_type_1.json b/src/Symfony/Component/Form/Tests/Fixtures/Descriptor/resolved_form_type_1.json index 3cd6ca5ca4..2b51a6e40b 100644 --- a/src/Symfony/Component/Form/Tests/Fixtures/Descriptor/resolved_form_type_1.json +++ b/src/Symfony/Component/Form/Tests/Fixtures/Descriptor/resolved_form_type_1.json @@ -40,6 +40,7 @@ "by_reference", "data", "disabled", + "form_attr", "getter", "help", "help_attr", diff --git a/src/Symfony/Component/Form/Tests/Fixtures/Descriptor/resolved_form_type_1.txt b/src/Symfony/Component/Form/Tests/Fixtures/Descriptor/resolved_form_type_1.txt index 0952ccc3e5..42e858528e 100644 --- a/src/Symfony/Component/Form/Tests/Fixtures/Descriptor/resolved_form_type_1.txt +++ b/src/Symfony/Component/Form/Tests/Fixtures/Descriptor/resolved_form_type_1.txt @@ -17,8 +17,9 @@ Symfony\Component\Form\Extension\Core\Type\ChoiceType (Block prefix: "choice") expanded by_reference group_by data multiple disabled - placeholder getter - preferred_choices help + placeholder form_attr + preferred_choices getter + help help_attr help_html help_translation_parameters diff --git a/src/Symfony/Component/Form/Tests/Fixtures/Descriptor/resolved_form_type_2.json b/src/Symfony/Component/Form/Tests/Fixtures/Descriptor/resolved_form_type_2.json index 9ac279de87..5bb64c00fd 100644 --- a/src/Symfony/Component/Form/Tests/Fixtures/Descriptor/resolved_form_type_2.json +++ b/src/Symfony/Component/Form/Tests/Fixtures/Descriptor/resolved_form_type_2.json @@ -17,6 +17,7 @@ "disabled", "empty_data", "error_bubbling", + "form_attr", "getter", "help", "help_attr", diff --git a/src/Symfony/Component/Form/Tests/Fixtures/Descriptor/resolved_form_type_2.txt b/src/Symfony/Component/Form/Tests/Fixtures/Descriptor/resolved_form_type_2.txt index c5ec9f7758..30ba52c92a 100644 --- a/src/Symfony/Component/Form/Tests/Fixtures/Descriptor/resolved_form_type_2.txt +++ b/src/Symfony/Component/Form/Tests/Fixtures/Descriptor/resolved_form_type_2.txt @@ -19,6 +19,7 @@ Symfony\Component\Form\Extension\Core\Type\FormType (Block prefix: "form") disabled empty_data error_bubbling + form_attr getter help help_attr