From 76bbcaf97bdaa4545c28217ec283e514b1e38a2b Mon Sep 17 00:00:00 2001 From: Victor Berchet Date: Fri, 27 Apr 2012 11:22:39 +0200 Subject: [PATCH 1/8] [Form] Revert "merged branch bschussek/issue3990 (PR #3996)" This reverts commit aebaece46063e62124e0ce585c07fd3004943c67, reversing changes made to fd52f93c7e3b95423645363afcbec0e0d50e2efc. --- .../Csrf/Type/FormTypeCsrfExtension.php | 4 +- .../Form/Tests/AbstractDivLayoutTest.php | 64 ++++++++-------- .../Form/Tests/AbstractLayoutTest.php | 16 ++-- .../Form/Tests/AbstractTableLayoutTest.php | 74 +++++++++---------- .../Csrf/Type/FormTypeCsrfExtensionTest.php | 35 --------- 5 files changed, 79 insertions(+), 114 deletions(-) diff --git a/src/Symfony/Component/Form/Extension/Csrf/Type/FormTypeCsrfExtension.php b/src/Symfony/Component/Form/Extension/Csrf/Type/FormTypeCsrfExtension.php index 8145e8e5755d6..f0c3d8e312dcc 100644 --- a/src/Symfony/Component/Form/Extension/Csrf/Type/FormTypeCsrfExtension.php +++ b/src/Symfony/Component/Form/Extension/Csrf/Type/FormTypeCsrfExtension.php @@ -62,9 +62,9 @@ public function buildForm(FormBuilder $builder, array $options) * @param FormView $view The form view * @param FormInterface $form The form */ - public function buildViewBottomUp(FormView $view, FormInterface $form) + public function buildView(FormView $view, FormInterface $form) { - if (!$view->hasParent() && $view->hasChildren() && $form->hasAttribute('csrf_field_name')) { + if ($form->isRoot() && $form->hasChildren() && $form->hasAttribute('csrf_field_name')) { $name = $form->getAttribute('csrf_field_name'); $csrfProvider = $form->getAttribute('csrf_provider'); $intention = $form->getAttribute('csrf_intention'); diff --git a/src/Symfony/Component/Form/Tests/AbstractDivLayoutTest.php b/src/Symfony/Component/Form/Tests/AbstractDivLayoutTest.php index 07813061886ba..7d1512f0037bd 100644 --- a/src/Symfony/Component/Form/Tests/AbstractDivLayoutTest.php +++ b/src/Symfony/Component/Form/Tests/AbstractDivLayoutTest.php @@ -96,7 +96,10 @@ public function testRest() $html = $this->renderRest($view); $this->assertMatchesXpath($html, -'/div +'/input + [@type="hidden"] + [@id="name__token"] +/following-sibling::div [ ./label[@for="name_field1"] /following-sibling::input[@type="text"][@id="name_field1"] @@ -109,9 +112,6 @@ public function testRest() [count(../div)=2] [count(..//label)=2] [count(..//input)=3] -/following-sibling::input - [@type="hidden"] - [@id="name__token"] ' ); } @@ -144,7 +144,8 @@ public function testRestWithChildrenForms() $html = $this->renderRest($view); $this->assertMatchesXpath($html, -'/div +'/input[@type="hidden"][@id="parent__token"] +/following-sibling::div [ ./label[not(@for)] /following-sibling::div[@id="parent_child1"] @@ -171,7 +172,6 @@ public function testRestWithChildrenForms() ] [count(//label)=4] [count(//input[@type="text"])=2] -/following-sibling::input[@type="hidden"][@id="parent__token"] ' ); } @@ -189,15 +189,15 @@ public function testRestAndRepeatedWithRow() $html = $this->renderRest($view); $this->assertMatchesXpath($html, -'/div +'/input + [@type="hidden"] + [@id="name__token"] +/following-sibling::div [ ./label[@for="name_first"] /following-sibling::input[@type="text"][@id="name_first"] ] [count(.//input)=1] -/following-sibling::input - [@type="hidden"] - [@id="name__token"] ' ); } @@ -216,16 +216,16 @@ public function testRestAndRepeatedWithRowPerChild() $html = $this->renderRest($view); $this->assertMatchesXpath($html, -'/div +'/input + [@type="hidden"] + [@id="name__token"] +/following-sibling::div [ ./label[@for="name_first"] /following-sibling::input[@type="text"][@id="name_first"] ] [count(.//input)=1] [count(.//label)=1] -/following-sibling::input - [@type="hidden"] - [@id="name__token"] ' ); } @@ -246,16 +246,16 @@ public function testRestAndRepeatedWithWidgetPerChild() $html = $this->renderRest($view); $this->assertMatchesXpath($html, -'/div +'/input + [@type="hidden"] + [@id="name__token"] +/following-sibling::div [ ./label[@for="name_first"] /following-sibling::input[@type="text"][@id="name_first"] ] [count(//input)=2] [count(//label)=1] -/following-sibling::input - [@type="hidden"] - [@id="name__token"] ' ); } @@ -293,7 +293,8 @@ public function testCollectionRow() $this->assertWidgetMatchesXpath($form->createView(), array(), '/div [ - ./div + ./input[@type="hidden"][@id="form__token"] + /following-sibling::div [ ./label[not(@for)] /following-sibling::div @@ -310,7 +311,6 @@ public function testCollectionRow() ] ] ] - /following-sibling::input[@type="hidden"][@id="form__token"] ] [count(.//input)=3] ' @@ -327,7 +327,8 @@ public function testForm() $this->assertWidgetMatchesXpath($form->createView(), array(), '/div [ - ./div + ./input[@type="hidden"][@id="name__token"] + /following-sibling::div [ ./label[@for="name_firstName"] /following-sibling::input[@type="text"][@id="name_firstName"] @@ -337,7 +338,6 @@ public function testForm() ./label[@for="name_lastName"] /following-sibling::input[@type="text"][@id="name_lastName"] ] - /following-sibling::input[@type="hidden"][@id="name__token"] ] [count(.//input)=3] ' @@ -383,8 +383,8 @@ public function testCsrf() $this->assertWidgetMatchesXpath($form->createView(), array(), '/div [ - ./div - /following-sibling::input[@type="hidden"][@id="name__token"][@value="foo&bar"] + ./input[@type="hidden"][@id="name__token"][@value="foo&bar"] + /following-sibling::div ] [count(.//input[@type="hidden"])=1] ' @@ -400,7 +400,8 @@ public function testRepeated() $this->assertWidgetMatchesXpath($form->createView(), array(), '/div [ - ./div + ./input[@type="hidden"][@id="name__token"] + /following-sibling::div [ ./label[@for="name_first"] /following-sibling::input[@type="text"][@id="name_first"] @@ -410,7 +411,6 @@ public function testRepeated() ./label[@for="name_second"] /following-sibling::input[@type="text"][@id="name_second"] ] - /following-sibling::input[@type="hidden"][@id="name__token"] ] [count(.//input)=3] ' @@ -428,7 +428,8 @@ public function testRepeatedWithCustomOptions() $this->assertWidgetMatchesXpath($form->createView(), array(), '/div [ - ./div + ./input[@type="hidden"][@id="name__token"] + /following-sibling::div [ ./label[@for="name_first"][.="[trans]Test[/trans]"] /following-sibling::input[@type="text"][@id="name_first"][@required="required"] @@ -438,7 +439,6 @@ public function testRepeatedWithCustomOptions() ./label[@for="name_second"][.="[trans]Test2[/trans]"] /following-sibling::input[@type="text"][@id="name_second"][@required="required"] ] - /following-sibling::input[@type="hidden"][@id="name__token"] ] [count(.//input)=3] ' @@ -454,12 +454,12 @@ public function testSearchInputName() $this->assertWidgetMatchesXpath($form->createView(), array(), '/div [ - ./div + ./input[@type="hidden"][@id="full__token"] + /following-sibling::div [ ./label[@for="full_name"] /following-sibling::input[@type="search"][@id="full_name"][@name="full[name]"] ] - /following-sibling::input[@type="hidden"][@id="full__token"] ] [count(//input)=2] ' @@ -521,7 +521,8 @@ public function testThemeInheritance($parentTheme, $childTheme) $this->assertWidgetMatchesXpath($view, array(), '/div [ - ./div + ./input[@type="hidden"] + /following-sibling::div [ ./label[.="parent"] /following-sibling::input[@type="text"] @@ -538,7 +539,6 @@ public function testThemeInheritance($parentTheme, $childTheme) ] ] ] - /following-sibling::input[@type="hidden"] ] ' ); diff --git a/src/Symfony/Component/Form/Tests/AbstractLayoutTest.php b/src/Symfony/Component/Form/Tests/AbstractLayoutTest.php index c75ba7c7a4ff3..5d48b9921cffb 100644 --- a/src/Symfony/Component/Form/Tests/AbstractLayoutTest.php +++ b/src/Symfony/Component/Form/Tests/AbstractLayoutTest.php @@ -646,11 +646,11 @@ public function testSingleChoiceExpanded() $this->assertWidgetMatchesXpath($form->createView(), array(), '/div [ - ./input[@type="radio"][@name="name"][@id="name_0"][@value="&a"][@checked] + ./input[@type="hidden"][@id="name__token"] + /following-sibling::input[@type="radio"][@name="name"][@id="name_0"][@value="&a"][@checked] /following-sibling::label[@for="name_0"][.="[trans]Choice&A[/trans]"] /following-sibling::input[@type="radio"][@name="name"][@id="name_1"][@value="&b"][not(@checked)] /following-sibling::label[@for="name_1"][.="[trans]Choice&B[/trans]"] - /following-sibling::input[@type="hidden"][@id="name__token"] ] [count(./input)=3] ' @@ -669,11 +669,11 @@ public function testSingleChoiceExpandedSkipEmptyValue() $this->assertWidgetMatchesXpath($form->createView(), array(), '/div [ - ./input[@type="radio"][@name="name"][@id="name_0"][@checked] + ./input[@type="hidden"][@id="name__token"] + /following-sibling::input[@type="radio"][@name="name"][@id="name_0"][@checked] /following-sibling::label[@for="name_0"][.="[trans]Choice&A[/trans]"] /following-sibling::input[@type="radio"][@name="name"][@id="name_1"][not(@checked)] /following-sibling::label[@for="name_1"][.="[trans]Choice&B[/trans]"] - /following-sibling::input[@type="hidden"][@id="name__token"] ] [count(./input)=3] ' @@ -691,11 +691,11 @@ public function testSingleChoiceExpandedWithBooleanValue() $this->assertWidgetMatchesXpath($form->createView(), array(), '/div [ - ./input[@type="radio"][@name="name"][@id="name_0"][@checked] + ./input[@type="hidden"][@id="name__token"] + /following-sibling::input[@type="radio"][@name="name"][@id="name_0"][@checked] /following-sibling::label[@for="name_0"][.="[trans]Choice&A[/trans]"] /following-sibling::input[@type="radio"][@name="name"][@id="name_1"][not(@checked)] /following-sibling::label[@for="name_1"][.="[trans]Choice&B[/trans]"] - /following-sibling::input[@type="hidden"][@id="name__token"] ] [count(./input)=3] ' @@ -714,13 +714,13 @@ public function testMultipleChoiceExpanded() $this->assertWidgetMatchesXpath($form->createView(), array(), '/div [ - ./input[@type="checkbox"][@name="name[]"][@id="name_0"][@checked][not(@required)] + ./input[@type="hidden"][@id="name__token"] + /following-sibling::input[@type="checkbox"][@name="name[]"][@id="name_0"][@checked][not(@required)] /following-sibling::label[@for="name_0"][.="[trans]Choice&A[/trans]"] /following-sibling::input[@type="checkbox"][@name="name[]"][@id="name_1"][not(@checked)][not(@required)] /following-sibling::label[@for="name_1"][.="[trans]Choice&B[/trans]"] /following-sibling::input[@type="checkbox"][@name="name[]"][@id="name_2"][@checked][not(@required)] /following-sibling::label[@for="name_2"][.="[trans]Choice&C[/trans]"] - /following-sibling::input[@type="hidden"][@id="name__token"] ] [count(./input)=4] ' diff --git a/src/Symfony/Component/Form/Tests/AbstractTableLayoutTest.php b/src/Symfony/Component/Form/Tests/AbstractTableLayoutTest.php index f6edbce25a440..0943b06c4162d 100644 --- a/src/Symfony/Component/Form/Tests/AbstractTableLayoutTest.php +++ b/src/Symfony/Component/Form/Tests/AbstractTableLayoutTest.php @@ -45,7 +45,12 @@ public function testRepeatedRow() $html = $this->renderRow($form->createView()); $this->assertMatchesXpath($html, -'/tr +'/tr[@style="display: none"] + [./td[@colspan="2"]/input + [@type="hidden"] + [@id="name__token"] + ] +/following-sibling::tr [ ./td [./label[@for="name_first"]] @@ -60,11 +65,6 @@ public function testRepeatedRow() [./input[@id="name_second"]] ] [count(../tr)=3] -/following-sibling::tr[@style="display: none"] - [./td[@colspan="2"]/input - [@type="hidden"] - [@id="name__token"] - ] ' ); } @@ -81,6 +81,11 @@ public function testRepeatedRowWithErrors() [./td[@colspan="2"]/ul [./li[.="[trans]Error![/trans]"]] ] +/following-sibling::tr[@style="display: none"] + [./td[@colspan="2"]/input + [@type="hidden"] + [@id="name__token"] + ] /following-sibling::tr [ ./td @@ -96,11 +101,6 @@ public function testRepeatedRowWithErrors() [./input[@id="name_second"]] ] [count(../tr)=4] -/following-sibling::tr[@style="display: none"] - [./td[@colspan="2"]/input - [@type="hidden"] - [@id="name__token"] - ] ' ); } @@ -126,7 +126,12 @@ public function testRest() $html = $this->renderRest($view); $this->assertMatchesXpath($html, -'/tr +'/tr[@style="display: none"] + [./td[@colspan="2"]/input + [@type="hidden"] + [@id="name__token"] + ] +/following-sibling::tr [ ./td [./label[@for="name_field1"]] @@ -143,11 +148,6 @@ public function testRest() [count(../tr)=3] [count(..//label)=2] [count(..//input)=3] -/following-sibling::tr[@style="display: none"] - [./td[@colspan="2"]/input - [@type="hidden"] - [@id="name__token"] - ] ' ); } @@ -161,9 +161,9 @@ public function testCollection() $this->assertWidgetMatchesXpath($form->createView(), array(), '/table [ - ./tr[./td/input[@type="text"][@value="a"]] + ./tr[@style="display: none"][./td[@colspan="2"]/input[@type="hidden"][@id="name__token"]] + /following-sibling::tr[./td/input[@type="text"][@value="a"]] /following-sibling::tr[./td/input[@type="text"][@value="b"]] - /following-sibling::tr[@style="display: none"][./td[@colspan="2"]/input[@type="hidden"][@id="name__token"]] ] [count(./tr[./td/input])=3] ' @@ -181,7 +181,12 @@ public function testForm() $this->assertWidgetMatchesXpath($view, array(), '/table [ - ./tr + ./tr[@style="display: none"] + [./td[@colspan="2"]/input + [@type="hidden"] + [@id="name__token"] + ] + /following-sibling::tr [ ./td [./label[@for="name_firstName"]] @@ -195,11 +200,6 @@ public function testForm() /following-sibling::td [./input[@id="name_lastName"]] ] - /following-sibling::tr[@style="display: none"] - [./td[@colspan="2"]/input - [@type="hidden"] - [@id="name__token"] - ] ] [count(.//input)=3] ' @@ -267,7 +267,12 @@ public function testRepeated() $this->assertWidgetMatchesXpath($form->createView(), array(), '/table [ - ./tr + ./tr[@style="display: none"] + [./td[@colspan="2"]/input + [@type="hidden"] + [@id="name__token"] + ] + /following-sibling::tr [ ./td [./label[@for="name_first"]] @@ -281,11 +286,6 @@ public function testRepeated() /following-sibling::td [./input[@type="text"][@id="name_second"]] ] - /following-sibling::tr[@style="display: none"] - [./td[@colspan="2"]/input - [@type="hidden"] - [@id="name__token"] - ] ] [count(.//input)=3] ' @@ -303,7 +303,12 @@ public function testRepeatedWithCustomOptions() $this->assertWidgetMatchesXpath($form->createView(), array(), '/table [ - ./tr + ./tr[@style="display: none"] + [./td[@colspan="2"]/input + [@type="hidden"] + [@id="name__token"] + ] + /following-sibling::tr [ ./td [./label[@for="name_first"][.="[trans]Test[/trans]"]] @@ -317,11 +322,6 @@ public function testRepeatedWithCustomOptions() /following-sibling::td [./input[@type="password"][@id="name_second"][@required="required"]] ] - /following-sibling::tr[@style="display: none"] - [./td[@colspan="2"]/input - [@type="hidden"] - [@id="name__token"] - ] ] [count(.//input)=3] ' diff --git a/src/Symfony/Component/Form/Tests/Extension/Csrf/Type/FormTypeCsrfExtensionTest.php b/src/Symfony/Component/Form/Tests/Extension/Csrf/Type/FormTypeCsrfExtensionTest.php index 4424f81e14140..9024002b5223b 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Csrf/Type/FormTypeCsrfExtensionTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Csrf/Type/FormTypeCsrfExtensionTest.php @@ -11,26 +11,9 @@ namespace Symfony\Component\Form\Tests\Extension\Csrf\Type; -use Symfony\Component\Form\AbstractType; -use Symfony\Component\Form\FormBuilder; use Symfony\Component\Form\Extension\Csrf\CsrfExtension; use Symfony\Component\Form\Tests\Extension\Core\Type\TypeTestCase; -class FormTypeCsrfExtensionTest_ChildType extends AbstractType -{ - public function buildForm(FormBuilder $builder, array $options) - { - // The form needs a child in order to trigger CSRF protection by - // default - $builder->add('name', 'text'); - } - - public function getName() - { - return 'csrf_collection_test'; - } -} - class FormTypeCsrfExtensionTest extends TypeTestCase { protected $csrfProvider; @@ -212,22 +195,4 @@ public function testDontValidateTokenIfRootButNoChildren() 'csrf' => 'token', )); } - - public function testNoCsrfProtectionOnPrototype() - { - $prototypeView = $this->factory - ->create('collection', null, array( - 'type' => new FormTypeCsrfExtensionTest_ChildType(), - 'options' => array( - 'csrf_field_name' => 'csrf', - ), - 'prototype' => true, - 'allow_add' => true, - )) - ->createView() - ->get('prototype'); - - $this->assertFalse($prototypeView->hasChild('csrf')); - $this->assertCount(1, $prototypeView); - } } From a47148b4a3b2b607b1ff95c1b723307f72c4fc44 Mon Sep 17 00:00:00 2001 From: Victor Berchet Date: Fri, 27 Apr 2012 11:23:33 +0200 Subject: [PATCH 2/8] [Form] Revert "merged branch vicb/FormTypeValidatorExtension_fix (PR #3984)" This reverts commit 83a0edd24bcecf5155c71bd34aee55a0ee28c454, reversing changes made to 065632b059bf99548d5d6cd284667f1da207c9f9. --- src/Symfony/Bundle/FrameworkBundle/Resources/config/form.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/form.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/form.xml index 57f6f42ca8da5..bddb2a96e809a 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/form.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/form.xml @@ -134,7 +134,7 @@ - + From d3610724b3e32cde40b6eddcf5962ec9dbb17045 Mon Sep 17 00:00:00 2001 From: Victor Berchet Date: Fri, 27 Apr 2012 13:00:47 +0200 Subject: [PATCH 3/8] [Form] Revert "merged branch bschussek/issue3878 (PR #3923)" This reverts commit 85bb43935669eb1e92a5b32f5f20872f34377300, reversing changes made to 962f975a6f5b0e4ed604d5b8c59d5dfca3f05e98. Conflicts: CHANGELOG-2.1.md UPGRADE-2.1.md src/Symfony/Component/Form/Extension/Core/Type/FormType.php src/Symfony/Component/Form/Tests/FormFactoryTest.php s3923 --- UPGRADE-2.1.md | 39 +- .../views/Form/form_div_layout.html.twig | 135 +++--- .../views/Form/form_table_layout.html.twig | 45 +- .../Tests/Extension/child_label.html.twig | 4 +- .../Tests/Extension/parent_label.html.twig | 4 +- .../Twig/Tests/Extension/theme.html.twig | 4 +- .../Tests/Extension/theme_extends.html.twig | 4 +- .../Twig/Tests/Extension/theme_use.html.twig | 4 +- .../FrameworkBundle/Resources/config/form.xml | 5 +- .../Resources/config/form_csrf.xml | 5 +- .../Resources/views/Form/date_widget.html.php | 2 +- .../views/Form/datetime_widget.html.php | 2 +- .../views/Form/email_widget.html.php | 2 +- .../views/Form/field_enctype.html.php | 2 +- .../views/Form/field_errors.html.php | 22 +- .../Resources/views/Form/field_label.html.php | 3 +- .../Resources/views/Form/field_rest.html.php | 6 +- .../Resources/views/Form/field_row.html.php | 6 +- .../Resources/views/Form/field_rows.html.php | 5 +- .../views/Form/field_widget.html.php | 6 +- .../views/Form/form_enctype.html.php | 1 - .../Resources/views/Form/form_errors.html.php | 21 - .../Resources/views/Form/form_label.html.php | 1 - .../Resources/views/Form/form_rest.html.php | 5 - .../Resources/views/Form/form_row.html.php | 3 - .../Resources/views/Form/form_rows.html.php | 4 - .../Resources/views/Form/form_widget.html.php | 7 +- .../views/Form/hidden_widget.html.php | 2 +- .../Resources/views/Form/input.html.php | 5 - .../views/Form/integer_widget.html.php | 2 +- .../views/Form/money_widget.html.php | 2 +- .../views/Form/number_widget.html.php | 2 +- .../views/Form/password_widget.html.php | 2 +- .../views/Form/percent_widget.html.php | 2 +- .../views/Form/repeated_row.html.php | 2 +- .../views/Form/search_widget.html.php | 2 +- .../Resources/views/Form/time_widget.html.php | 2 +- .../Resources/views/Form/url_widget.html.php | 2 +- .../views/FormTable/field_row.html.php | 9 + .../views/FormTable/form_errors.html.php | 58 +-- .../views/FormTable/form_row.html.php | 3 - .../views/FormTable/form_widget.html.php | 7 +- .../Templating/Helper/FormHelper.php | 6 +- .../FrameworkExtensionTest.php | 4 +- ...rm_label.html.php => field_label.html.php} | 0 ...rm_label.html.php => field_label.html.php} | 0 .../{input.html.php => field_widget.html.php} | 0 .../Form/DataTransformerInterface.php | 4 +- .../Form/Exception/DanglingFieldException.php | 21 + .../Exception/FieldDefinitionException.php | 16 + .../Form/Extension/Core/Type/ChoiceType.php | 10 +- .../Form/Extension/Core/Type/DateTimeType.php | 4 +- .../Form/Extension/Core/Type/DateType.php | 4 +- .../Form/Extension/Core/Type/FieldType.php | 183 ++++++++- .../Form/Extension/Core/Type/FileType.php | 14 +- .../Form/Extension/Core/Type/FormType.php | 166 +------- .../Form/Extension/Core/Type/TimeType.php | 4 +- .../Form/Extension/Csrf/CsrfExtension.php | 16 +- .../EventListener/CsrfValidationListener.php | 26 +- .../EventListener/EnsureCsrfFieldListener.php | 66 +++ .../Csrf/Type/ChoiceTypeCsrfExtension.php | 27 ++ .../Form/Extension/Csrf/Type/CsrfType.php | 83 ++++ .../Csrf/Type/DateTypeCsrfExtension.php | 27 ++ .../Csrf/Type/FormTypeCsrfExtension.php | 54 ++- .../Csrf/Type/RepeatedTypeCsrfExtension.php | 27 ++ .../Csrf/Type/TimeTypeCsrfExtension.php | 27 ++ ...on.php => FieldTypeValidatorExtension.php} | 4 +- .../Validator/ValidatorExtension.php | 2 +- src/Symfony/Component/Form/Form.php | 52 +-- src/Symfony/Component/Form/FormBuilder.php | 4 +- src/Symfony/Component/Form/FormInterface.php | 2 +- .../Component/Form/FormTypeGuesserChain.php | 2 +- src/Symfony/Component/Form/FormView.php | 55 +-- .../Form/Tests/AbstractDivLayoutTest.php | 44 +- .../Form/Tests/AbstractLayoutTest.php | 36 +- .../Form/Tests/AbstractTableLayoutTest.php | 71 +--- .../Extension/Core/Type/ChoiceTypeTest.php | 12 +- .../Core/Type/CollectionTypeTest.php | 22 +- .../Extension/Core/Type/FieldTypeTest.php | 380 +++++++++++++++++ .../Extension/Core/Type/FormTypeTest.php | 383 +----------------- .../Extension/Core/Type/RepeatedTypeTest.php | 14 +- .../EnsureCsrfFieldListenerTest.php | 87 ++++ .../Extension/Csrf/Type/CsrfTypeTest.php | 112 +++++ .../Csrf/Type/FormTypeCsrfExtensionTest.php | 181 +-------- .../Extension/Csrf/Type/TypeTestCase.php | 41 ++ .../DelegatingValidationListenerTest.php | 1 - ...hp => FieldTypeValidatorExtensionTest.php} | 16 +- .../Component/Form/Tests/FormBuilderTest.php | 2 +- .../Component/Form/Tests/FormFactoryTest.php | 4 +- src/Symfony/Component/Form/Tests/FormTest.php | 35 +- 90 files changed, 1486 insertions(+), 1314 deletions(-) delete mode 100644 src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/form_enctype.html.php delete mode 100644 src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/form_errors.html.php delete mode 100644 src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/form_rest.html.php delete mode 100644 src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/form_rows.html.php delete mode 100644 src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/input.html.php create mode 100644 src/Symfony/Bundle/FrameworkBundle/Resources/views/FormTable/field_row.html.php rename src/Symfony/Bundle/FrameworkBundle/Tests/Templating/Helper/Resources/Child/{form_label.html.php => field_label.html.php} (100%) rename src/Symfony/Bundle/FrameworkBundle/Tests/Templating/Helper/Resources/Parent/{form_label.html.php => field_label.html.php} (100%) rename src/Symfony/Bundle/FrameworkBundle/Tests/Templating/Helper/Resources/Parent/{input.html.php => field_widget.html.php} (100%) create mode 100644 src/Symfony/Component/Form/Exception/DanglingFieldException.php create mode 100644 src/Symfony/Component/Form/Exception/FieldDefinitionException.php create mode 100644 src/Symfony/Component/Form/Extension/Csrf/EventListener/EnsureCsrfFieldListener.php create mode 100644 src/Symfony/Component/Form/Extension/Csrf/Type/ChoiceTypeCsrfExtension.php create mode 100644 src/Symfony/Component/Form/Extension/Csrf/Type/CsrfType.php create mode 100644 src/Symfony/Component/Form/Extension/Csrf/Type/DateTypeCsrfExtension.php create mode 100644 src/Symfony/Component/Form/Extension/Csrf/Type/RepeatedTypeCsrfExtension.php create mode 100644 src/Symfony/Component/Form/Extension/Csrf/Type/TimeTypeCsrfExtension.php rename src/Symfony/Component/Form/Extension/Validator/Type/{FormTypeValidatorExtension.php => FieldTypeValidatorExtension.php} (95%) create mode 100644 src/Symfony/Component/Form/Tests/Extension/Core/Type/FieldTypeTest.php create mode 100644 src/Symfony/Component/Form/Tests/Extension/Csrf/EventListener/EnsureCsrfFieldListenerTest.php create mode 100644 src/Symfony/Component/Form/Tests/Extension/Csrf/Type/CsrfTypeTest.php create mode 100644 src/Symfony/Component/Form/Tests/Extension/Csrf/Type/TypeTestCase.php rename src/Symfony/Component/Form/Tests/Extension/Validator/Type/{FormTypeValidatorExtensionTest.php => FieldTypeValidatorExtensionTest.php} (79%) diff --git a/UPGRADE-2.1.md b/UPGRADE-2.1.md index 29baddf4f6c15..df46f4cebc59d 100644 --- a/UPGRADE-2.1.md +++ b/UPGRADE-2.1.md @@ -102,7 +102,7 @@ ``` * The custom factories for the firewall configuration are now registered during the build method of bundles instead of being registered - by the end-user. This means that you will you need to remove the 'factories' + by the end-user. This means that you will you need to remove the 'factories' keys in your security configuration. * The Firewall listener is now registered after the Router listener. This @@ -313,29 +313,29 @@ return isset($options['widget']) && 'single_text' === $options['widget'] ? 'text' : 'choice'; } ``` - + * The methods `getDefaultOptions()` and `getAllowedOptionValues()` of form types no longer receive an option array. - + You can specify options that depend on other options using closures instead. - + Before: - + ``` public function getDefaultOptions(array $options) { $defaultOptions = array(); - + if ($options['multiple']) { $defaultOptions['empty_data'] = array(); } - + return $defaultOptions; } ``` - + After: - + ``` public function getDefaultOptions() { @@ -346,7 +346,7 @@ ); } ``` - + The second argument `$previousValue` does not have to be specified if not needed. @@ -366,29 +366,22 @@ (or any other of the BIND events). In case you used the CallbackValidator class, you should now pass the callback directly to `addEventListener`. - * simplified CSRF protection and removed the csrf type - - * deprecated FieldType and merged it into FormType - - * [BC BREAK] renamed "field_*" theme blocks to "form_*" and "field_widget" to - "input" - * The method `guessMinLength()` of FormTypeGuesserInterface was deprecated and will be removed in Symfony 2.3. You should use the new method `guessPattern()` instead which may return any regular expression that is inserted in the HTML5 attribute "pattern". - + Before: - + public function guessMinLength($class, $property) { if (/* condition */) { return new ValueGuess($minLength, Guess::LOW_CONFIDENCE); } } - + After: - + public function guessPattern($class, $property) { if (/* condition */) { @@ -470,7 +463,7 @@ `validate` and its return value was dropped. `ConstraintValidator` still contains the deprecated `isValid` method and - forwards `validate` calls to `isValid` by default. This BC layer will be + forwards `validate` calls to `isValid` by default. This BC layer will be removed in Symfony 2.3. You are advised to rename your methods. You should also remove the return values, which have never been used by the framework. @@ -500,7 +493,7 @@ $this->context->addViolation($constraint->message, array( '{{ value }}' => $value, )); - + return; } } diff --git a/src/Symfony/Bridge/Twig/Resources/views/Form/form_div_layout.html.twig b/src/Symfony/Bridge/Twig/Resources/views/Form/form_div_layout.html.twig index 46420a71221fd..cc3f3fd01d688 100644 --- a/src/Symfony/Bridge/Twig/Resources/views/Form/form_div_layout.html.twig +++ b/src/Symfony/Bridge/Twig/Resources/views/Form/form_div_layout.html.twig @@ -2,14 +2,10 @@ {% block form_widget %} {% spaceless %} - {% if form.children|length > 0 %} -
- {{ block('form_rows') }} - {{ form_rest(form) }} -
- {% else %} - {{ block('input') }} - {% endif %} +
+ {{ block('field_rows') }} + {{ form_rest(form) }} +
{% endspaceless %} {% endblock form_widget %} @@ -87,7 +83,7 @@ {% block datetime_widget %} {% spaceless %} {% if widget == 'single_text' %} - {{ block('input') }} + {{ block('field_widget') }} {% else %}
{{ form_errors(form.date) }} @@ -102,7 +98,7 @@ {% block date_widget %} {% spaceless %} {% if widget == 'single_text' %} - {{ block('input') }} + {{ block('field_widget') }} {% else %}
{{ date_pattern|replace({ @@ -118,7 +114,7 @@ {% block time_widget %} {% spaceless %} {% if widget == 'single_text' %} - {{ block('input') }} + {{ block('field_widget') }} {% else %}
{{ form_widget(form.hour, { 'attr': { 'size': '1' } }) }}:{{ form_widget(form.minute, { 'attr': { 'size': '1' } }) }}{% if with_seconds %}:{{ form_widget(form.second, { 'attr': { 'size': '1' } }) }}{% endif %} @@ -131,60 +127,67 @@ {% spaceless %} {# type="number" doesn't work with floats #} {% set type = type|default('text') %} - {{ block('input') }} + {{ block('field_widget') }} {% endspaceless %} {% endblock number_widget %} {% block integer_widget %} {% spaceless %} {% set type = type|default('number') %} - {{ block('input') }} + {{ block('field_widget') }} {% endspaceless %} {% endblock integer_widget %} {% block money_widget %} {% spaceless %} - {{ money_pattern|replace({ '{{ widget }}': block('input') })|raw }} + {{ money_pattern|replace({ '{{ widget }}': block('field_widget') })|raw }} {% endspaceless %} {% endblock money_widget %} {% block url_widget %} {% spaceless %} {% set type = type|default('url') %} - {{ block('input') }} + {{ block('field_widget') }} {% endspaceless %} {% endblock url_widget %} {% block search_widget %} {% spaceless %} {% set type = type|default('search') %} - {{ block('input') }} + {{ block('field_widget') }} {% endspaceless %} {% endblock search_widget %} {% block percent_widget %} {% spaceless %} {% set type = type|default('text') %} - {{ block('input') }} % + {{ block('field_widget') }} % {% endspaceless %} {% endblock percent_widget %} +{% block field_widget %} +{% spaceless %} + {% set type = type|default('text') %} + +{% endspaceless %} +{% endblock field_widget %} + {% block password_widget %} {% spaceless %} {% set type = type|default('password') %} - {{ block('input') }} + {{ block('field_widget') }} {% endspaceless %} {% endblock password_widget %} {% block hidden_widget %} {% set type = type|default('hidden') %} - {{ block('input') }} + {{ block('field_widget') }} {% endblock hidden_widget %} {% block email_widget %} {% spaceless %} {% set type = type|default('email') %} - {{ block('input') }} + {{ block('field_widget') }} {% endspaceless %} {% endblock email_widget %} @@ -199,11 +202,15 @@ {% endspaceless %} {% endblock %} +{% block field_label %} +{% spaceless %} + {% set attr = attr|merge({'for': id}) %} + {{ block('generic_label') }} +{% endspaceless %} +{% endblock field_label %} + {% block form_label %} {% spaceless %} - {% if form.children|length == 0 %} - {% set attr = attr|merge({'for': id}) %} - {% endif %} {{ block('generic_label') }} {% endspaceless %} {% endblock form_label %} @@ -212,17 +219,24 @@ {% block repeated_row %} {% spaceless %} - {{ block('form_rows') }} + {{ block('field_rows') }} {% endspaceless %} {% endblock repeated_row %} +{% block field_row %} +{% spaceless %} +
+ {{ form_label(form, label|default(null)) }} + {{ form_errors(form) }} + {{ form_widget(form) }} +
+{% endspaceless %} +{% endblock field_row %} + {% block form_row %} {% spaceless %}
{{ form_label(form, label|default(null)) }} - {% if form.children|length == 0 %} - {{ form_errors(form) }} - {% endif %} {{ form_widget(form) }}
{% endspaceless %} @@ -234,13 +248,13 @@ {# Misc #} -{% block form_enctype %} +{% block field_enctype %} {% spaceless %} {% if multipart %}enctype="multipart/form-data"{% endif %} {% endspaceless %} -{% endblock form_enctype %} +{% endblock field_enctype %} -{% block form_errors %} +{% block field_errors %} {% spaceless %} {% if errors|length > 0 %}
    @@ -256,9 +270,9 @@
{% endif %} {% endspaceless %} -{% endblock form_errors %} +{% endblock field_errors %} -{% block form_rest %} +{% block field_rest %} {% spaceless %} {% for child in form %} {% if not child.rendered %} @@ -266,25 +280,18 @@ {% endif %} {% endfor %} {% endspaceless %} -{% endblock form_rest %} +{% endblock field_rest %} {# Support #} -{% block form_rows %} +{% block field_rows %} {% spaceless %} {{ form_errors(form) }} {% for child in form %} {{ form_row(child) }} {% endfor %} {% endspaceless %} -{% endblock form_rows %} - -{% block input %} -{% spaceless %} - {% set type = type|default('text') %} - -{% endspaceless %} -{% endblock input %} +{% endblock field_rows %} {% block widget_attributes %} {% spaceless %} @@ -299,47 +306,3 @@ {% for attrname,attrvalue in attr %}{{attrname}}="{{attrvalue}}" {% endfor %} {% endspaceless %} {% endblock widget_container_attributes %} - -{# Deprecated in Symfony 2.1, to be removed in 2.3 #} - -{% block field_widget %} -{% spaceless %} - {{ block('input') }} -{% endspaceless %} -{% endblock field_widget %} - -{% block field_label %} -{% spaceless %} - {{ block('form_label') }} -{% endspaceless %} -{% endblock field_label %} - -{% block field_row %} -{% spaceless %} - {{ block('form_row') }} -{% endspaceless %} -{% endblock field_row %} - -{% block field_enctype %} -{% spaceless %} - {{ block('form_enctype') }} -{% endspaceless %} -{% endblock field_enctype %} - -{% block field_errors %} -{% spaceless %} - {{ block('form_errors') }} -{% endspaceless %} -{% endblock field_errors %} - -{% block field_rest %} -{% spaceless %} - {{ block('form_rest') }} -{% endspaceless %} -{% endblock field_rest %} - -{% block field_rows %} -{% spaceless %} - {{ block('form_rows') }} -{% endspaceless %} -{% endblock field_rows %} \ No newline at end of file diff --git a/src/Symfony/Bridge/Twig/Resources/views/Form/form_table_layout.html.twig b/src/Symfony/Bridge/Twig/Resources/views/Form/form_table_layout.html.twig index b053c59e6f7e1..1046c3de36225 100644 --- a/src/Symfony/Bridge/Twig/Resources/views/Form/form_table_layout.html.twig +++ b/src/Symfony/Bridge/Twig/Resources/views/Form/form_table_layout.html.twig @@ -1,5 +1,19 @@ {% use "form_div_layout.html.twig" %} +{% block field_row %} +{% spaceless %} + + + {{ form_label(form, label|default(null)) }} + + + {{ form_errors(form) }} + {{ form_widget(form) }} + + +{% endspaceless %} +{% endblock field_row %} + {% block form_row %} {% spaceless %} @@ -7,9 +21,6 @@ {{ form_label(form, label|default(null)) }} - {% if form.children|length == 0 %} - {{ form_errors(form) }} - {% endif %} {{ form_widget(form) }} @@ -18,16 +29,12 @@ {% block form_errors %} {% spaceless %} - {% if form.children|length > 0 %} - {% if errors|length > 0 %} - - - {{ parent() }} - - - {% endif %} - {% else %} - {{ parent() }} + {% if errors|length > 0 %} + + + {{ block('field_errors') }} + + {% endif %} {% endspaceless %} {% endblock form_errors %} @@ -44,13 +51,9 @@ {% block form_widget %} {% spaceless %} - {% if form.children|length > 0 %} - - {{ block('form_rows') }} - {{ form_rest(form) }} -
- {% else %} - {{ parent() }} - {% endif %} + + {{ block('field_rows') }} + {{ form_rest(form) }} +
{% endspaceless %} {% endblock form_widget %} diff --git a/src/Symfony/Bridge/Twig/Tests/Extension/child_label.html.twig b/src/Symfony/Bridge/Twig/Tests/Extension/child_label.html.twig index 061ef428c2942..16c137aaca26c 100644 --- a/src/Symfony/Bridge/Twig/Tests/Extension/child_label.html.twig +++ b/src/Symfony/Bridge/Twig/Tests/Extension/child_label.html.twig @@ -1,3 +1,3 @@ -{% block form_label %} +{% block field_label %} -{% endblock form_label %} +{% endblock field_label %} diff --git a/src/Symfony/Bridge/Twig/Tests/Extension/parent_label.html.twig b/src/Symfony/Bridge/Twig/Tests/Extension/parent_label.html.twig index e96278b8f6266..fc59d708bef60 100644 --- a/src/Symfony/Bridge/Twig/Tests/Extension/parent_label.html.twig +++ b/src/Symfony/Bridge/Twig/Tests/Extension/parent_label.html.twig @@ -1,3 +1,3 @@ -{% block form_label %} +{% block field_label %} -{% endblock form_label %} +{% endblock field_label %} diff --git a/src/Symfony/Bridge/Twig/Tests/Extension/theme.html.twig b/src/Symfony/Bridge/Twig/Tests/Extension/theme.html.twig index ee5b19e0737d8..d5016842ea186 100644 --- a/src/Symfony/Bridge/Twig/Tests/Extension/theme.html.twig +++ b/src/Symfony/Bridge/Twig/Tests/Extension/theme.html.twig @@ -1,6 +1,6 @@ -{% block input %} +{% block field_widget %} {% spaceless %} {% set type = type|default('text') %} {% endspaceless %} -{% endblock input %} +{% endblock field_widget %} diff --git a/src/Symfony/Bridge/Twig/Tests/Extension/theme_extends.html.twig b/src/Symfony/Bridge/Twig/Tests/Extension/theme_extends.html.twig index f58e589498dae..96bfea20dc0ef 100644 --- a/src/Symfony/Bridge/Twig/Tests/Extension/theme_extends.html.twig +++ b/src/Symfony/Bridge/Twig/Tests/Extension/theme_extends.html.twig @@ -1,8 +1,8 @@ {% extends 'form_div_layout.html.twig' %} -{% block input %} +{% block field_widget %} {% spaceless %} {% set type = type|default('text') %} {% endspaceless %} -{% endblock input %} +{% endblock field_widget %} diff --git a/src/Symfony/Bridge/Twig/Tests/Extension/theme_use.html.twig b/src/Symfony/Bridge/Twig/Tests/Extension/theme_use.html.twig index 9304e9dcfa94d..5aee4708a2cbf 100644 --- a/src/Symfony/Bridge/Twig/Tests/Extension/theme_use.html.twig +++ b/src/Symfony/Bridge/Twig/Tests/Extension/theme_use.html.twig @@ -1,8 +1,8 @@ {% use 'form_div_layout.html.twig' %} -{% block input %} +{% block field_widget %} {% spaceless %} {% set type = type|default('text') %} {% endspaceless %} -{% endblock input %} +{% endblock field_widget %} diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/form.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/form.xml index bddb2a96e809a..1422e792b8543 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/form.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/form.xml @@ -52,6 +52,7 @@ + @@ -132,8 +133,8 @@ - - + + diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/form_csrf.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/form_csrf.xml index 72442bcbba682..188a099df29a1 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/form_csrf.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/form_csrf.xml @@ -14,9 +14,12 @@ %kernel.secret% + + + + - %form.type_extension.csrf.enabled% %form.type_extension.csrf.field_name% diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/date_widget.html.php b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/date_widget.html.php index bd2c2769af420..4a4fa106e3a3d 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/date_widget.html.php +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/date_widget.html.php @@ -1,5 +1,5 @@ - renderBlock('input'); ?> + renderBlock('field_widget'); ?>
renderBlock('container_attributes') ?>> - renderBlock('input'); ?> + renderBlock('field_widget'); ?>
renderBlock('container_attributes') ?>> widget($form['date']).' '.$view['form']->widget($form['time']) ?> diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/email_widget.html.php b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/email_widget.html.php index a00dda278e983..c30cfb35c3f92 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/email_widget.html.php +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/email_widget.html.php @@ -1 +1 @@ -renderBlock('input', array('type' => isset($type) ? $type : 'email')) ?> +renderBlock('field_widget', array('type' => isset($type) ? $type : 'email')) ?> diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/field_enctype.html.php b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/field_enctype.html.php index aa4ff39bba6bd..424d4259693c5 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/field_enctype.html.php +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/field_enctype.html.php @@ -1 +1 @@ -renderBlock('form_enctype') ?> \ No newline at end of file +get('multipart')): ?>enctype="multipart/form-data" diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/field_errors.html.php b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/field_errors.html.php index d7b87ea2bc5f6..339e3d0009854 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/field_errors.html.php +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/field_errors.html.php @@ -1 +1,21 @@ -renderBlock('form_errors') ?> \ No newline at end of file + +
    + +
  • getMessagePluralization()) { + echo $view['translator']->trans( + $error->getMessageTemplate(), + $error->getMessageParameters(), + 'validators' + ); + } else { + echo $view['translator']->transChoice( + $error->getMessageTemplate(), + $error->getMessagePluralization(), + $error->getMessageParameters(), + 'validators' + ); + }?>
  • + +
+ diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/field_label.html.php b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/field_label.html.php index 7c0c1b7559cdc..1434301d4ca7f 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/field_label.html.php +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/field_label.html.php @@ -1 +1,2 @@ -renderBlock('form_label') ?> \ No newline at end of file + + diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/field_rest.html.php b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/field_rest.html.php index a570b1045a214..89041c6ec6374 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/field_rest.html.php +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/field_rest.html.php @@ -1 +1,5 @@ -renderBlock('form_rest') ?> \ No newline at end of file + + isRendered()): ?> + row($child) ?> + + diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/field_row.html.php b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/field_row.html.php index c91dcb554372a..091807020d23a 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/field_row.html.php +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/field_row.html.php @@ -1 +1,5 @@ -renderBlock('form_row') ?> \ No newline at end of file +
+ label($form, isset($label) ? $label : null) ?> + errors($form) ?> + widget($form) ?> +
diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/field_rows.html.php b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/field_rows.html.php index ef0bf384a6e76..a5f1dfbf5f178 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/field_rows.html.php +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/field_rows.html.php @@ -1 +1,4 @@ -renderBlock('form_rows') ?> \ No newline at end of file +errors($form) ?> + + row($child) ?> + diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/field_widget.html.php b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/field_widget.html.php index f1ca2edadbfda..0c8648338639a 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/field_widget.html.php +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/field_widget.html.php @@ -1 +1,5 @@ -renderBlock('input') ?> \ No newline at end of file +" + value="escape($value) ?>" + renderBlock('attributes') ?> +/> diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/form_enctype.html.php b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/form_enctype.html.php deleted file mode 100644 index 424d4259693c5..0000000000000 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/form_enctype.html.php +++ /dev/null @@ -1 +0,0 @@ -get('multipart')): ?>enctype="multipart/form-data" diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/form_errors.html.php b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/form_errors.html.php deleted file mode 100644 index 339e3d0009854..0000000000000 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/form_errors.html.php +++ /dev/null @@ -1,21 +0,0 @@ - -
    - -
  • getMessagePluralization()) { - echo $view['translator']->trans( - $error->getMessageTemplate(), - $error->getMessageParameters(), - 'validators' - ); - } else { - echo $view['translator']->transChoice( - $error->getMessageTemplate(), - $error->getMessagePluralization(), - $error->getMessageParameters(), - 'validators' - ); - }?>
  • - -
- diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/form_label.html.php b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/form_label.html.php index 3ddc300fd4540..e5c5843c46f53 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/form_label.html.php +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/form_label.html.php @@ -1,3 +1,2 @@ -hasChildren()) { $attr['for'] = $id; } ?> diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/form_rest.html.php b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/form_rest.html.php deleted file mode 100644 index 89041c6ec6374..0000000000000 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/form_rest.html.php +++ /dev/null @@ -1,5 +0,0 @@ - - isRendered()): ?> - row($child) ?> - - diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/form_row.html.php b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/form_row.html.php index 02fb9ae9b635c..0a79a0cc532a2 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/form_row.html.php +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/form_row.html.php @@ -1,7 +1,4 @@
label($form, isset($label) ? $label : null) ?> - hasChildren()): ?> - errors($form) ?> - widget($form) ?>
diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/form_rows.html.php b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/form_rows.html.php deleted file mode 100644 index a5f1dfbf5f178..0000000000000 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/form_rows.html.php +++ /dev/null @@ -1,4 +0,0 @@ -errors($form) ?> - - row($child) ?> - diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/form_widget.html.php b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/form_widget.html.php index 1c9368693b49c..77fa483c3fa97 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/form_widget.html.php +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/form_widget.html.php @@ -1,8 +1,5 @@ -hasChildren()): ?>
renderBlock('container_attributes') ?>> - renderBlock('form_rows') ?> + renderBlock('field_rows') ?> rest($form) ?>
- -renderBlock('input')?> - + diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/hidden_widget.html.php b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/hidden_widget.html.php index 50a42451aef26..11942cfe2b1b8 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/hidden_widget.html.php +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/hidden_widget.html.php @@ -1 +1 @@ -renderBlock('input', array('type' => isset($type) ? $type : "hidden")) ?> +renderBlock('field_widget', array('type' => isset($type) ? $type : "hidden")) ?> diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/input.html.php b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/input.html.php deleted file mode 100644 index 0c8648338639a..0000000000000 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/input.html.php +++ /dev/null @@ -1,5 +0,0 @@ -" - value="escape($value) ?>" - renderBlock('attributes') ?> -/> diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/integer_widget.html.php b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/integer_widget.html.php index 1fc6ace34b4fb..012211ab5ac2c 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/integer_widget.html.php +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/integer_widget.html.php @@ -1 +1 @@ -renderBlock('input', array('type' => isset($type) ? $type : "number")) ?> +renderBlock('field_widget', array('type' => isset($type) ? $type : "number")) ?> diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/money_widget.html.php b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/money_widget.html.php index a68ad5ddacb10..3151ecbd84776 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/money_widget.html.php +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/money_widget.html.php @@ -1 +1 @@ -renderBlock('input'), $money_pattern) ?> +renderBlock('field_widget'), $money_pattern) ?> diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/number_widget.html.php b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/number_widget.html.php index 7e1a2776a7d49..9a08222c91206 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/number_widget.html.php +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/number_widget.html.php @@ -1 +1 @@ -renderBlock('input', array('type' => isset($type) ? $type : "text")) ?> +renderBlock('field_widget', array('type' => isset($type) ? $type : "text")) ?> diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/password_widget.html.php b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/password_widget.html.php index 7aff242ef43cc..78319fcbaed42 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/password_widget.html.php +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/password_widget.html.php @@ -1 +1 @@ -renderBlock('input', array('type' => isset($type) ? $type : "password")) ?> +renderBlock('field_widget', array('type' => isset($type) ? $type : "password")) ?> diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/percent_widget.html.php b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/percent_widget.html.php index 328321f21fcae..4245b52a0c71a 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/percent_widget.html.php +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/percent_widget.html.php @@ -1 +1 @@ -renderBlock('input', array('type' => isset($type) ? $type : "text")) ?> % +renderBlock('field_widget', array('type' => isset($type) ? $type : "text")) ?> % diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/repeated_row.html.php b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/repeated_row.html.php index b9a07bc4666bc..a84bad54df978 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/repeated_row.html.php +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/repeated_row.html.php @@ -1 +1 @@ -renderBlock('form_rows') ?> +renderBlock('field_rows') ?> diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/search_widget.html.php b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/search_widget.html.php index d8a773544e713..bbfd593dbcc86 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/search_widget.html.php +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/search_widget.html.php @@ -1 +1 @@ -renderBlock('input', array('type' => isset($type) ? $type : "search")) ?> +renderBlock('field_widget', array('type' => isset($type) ? $type : "search")) ?> diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/time_widget.html.php b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/time_widget.html.php index 2178974c74c4c..599750d20aa65 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/time_widget.html.php +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/time_widget.html.php @@ -1,5 +1,5 @@ - renderBlock('input'); ?> + renderBlock('field_widget'); ?>
renderBlock('container_attributes') ?>> renderBlock('input', array('type' => isset($type) ? $type : "url")) ?> +renderBlock('field_widget', array('type' => isset($type) ? $type : "url")) ?> diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/views/FormTable/field_row.html.php b/src/Symfony/Bundle/FrameworkBundle/Resources/views/FormTable/field_row.html.php new file mode 100644 index 0000000000000..b9e5c5639ce46 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/views/FormTable/field_row.html.php @@ -0,0 +1,9 @@ + + + label($form, isset($label) ? $label : null) ?> + + + errors($form) ?> + widget($form) ?> + + diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/views/FormTable/form_errors.html.php b/src/Symfony/Bundle/FrameworkBundle/Resources/views/FormTable/form_errors.html.php index 05d8c4aea89d3..ac4315f957efa 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/views/FormTable/form_errors.html.php +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/views/FormTable/form_errors.html.php @@ -1,51 +1,7 @@ -hasChildren()): ?> - 0): ?> - - - -
    - -
  • getMessagePluralization()) { - echo $view['translator']->trans( - $error->getMessageTemplate(), - $error->getMessageParameters(), - 'validators' - ); - } else { - echo $view['translator']->transChoice( - $error->getMessageTemplate(), - $error->getMessagePluralization(), - $error->getMessageParameters(), - 'validators' - ); - }?>
  • - -
- - - - - - -
    - -
  • getMessagePluralization()) { - echo $view['translator']->trans( - $error->getMessageTemplate(), - $error->getMessageParameters(), - 'validators' - ); - } else { - echo $view['translator']->transChoice( - $error->getMessageTemplate(), - $error->getMessagePluralization(), - $error->getMessageParameters(), - 'validators' - ); - }?>
  • - -
- - \ No newline at end of file + + + + renderBlock('field_errors'); ?> + + + diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/views/FormTable/form_row.html.php b/src/Symfony/Bundle/FrameworkBundle/Resources/views/FormTable/form_row.html.php index 9262a1bae10b3..d1bbbb1b2a6f7 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/views/FormTable/form_row.html.php +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/views/FormTable/form_row.html.php @@ -3,9 +3,6 @@ label($form, isset($label) ? $label : null) ?> - hasChildren()): ?> - errors($form) ?> - widget($form) ?> diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/views/FormTable/form_widget.html.php b/src/Symfony/Bundle/FrameworkBundle/Resources/views/FormTable/form_widget.html.php index 171f1eb40c74e..d802ccf050960 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/views/FormTable/form_widget.html.php +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/views/FormTable/form_widget.html.php @@ -1,8 +1,5 @@ -hasChildren()): ?> renderBlock('container_attributes') ?>> - renderBlock('form_rows') ?> + renderBlock('field_rows') ?> rest($form) ?>
- -renderBlock('input')?> - + diff --git a/src/Symfony/Bundle/FrameworkBundle/Templating/Helper/FormHelper.php b/src/Symfony/Bundle/FrameworkBundle/Templating/Helper/FormHelper.php index 8fd4adef89d30..2004b4e67e3f1 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Templating/Helper/FormHelper.php +++ b/src/Symfony/Bundle/FrameworkBundle/Templating/Helper/FormHelper.php @@ -119,7 +119,7 @@ public function enctype(FormView $view) */ public function widget(FormView $view, array $variables = array()) { - return $this->renderSection($view, 'widget', $variables); + return trim($this->renderSection($view, 'widget', $variables)); } /** @@ -276,7 +276,7 @@ protected function renderSection(FormView $view, $section, array $variables = ar $view->setRendered(); } - return trim($html); + return $html; } } while (--$typeIndex >= 0); @@ -311,7 +311,7 @@ public function renderBlock($name, $variables = array()) $variables = array_replace_recursive($context['variables'], $variables); - return trim($this->engine->render($template, $variables)); + return $this->engine->render($template, $variables); } public function getName() diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php index 2c7204e7355eb..859ec489ab12d 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php @@ -27,9 +27,9 @@ public function testCsrfProtection() $def = $container->getDefinition('form.type_extension.csrf'); $this->assertTrue($container->getParameter('form.type_extension.csrf.enabled')); - $this->assertEquals('%form.type_extension.csrf.enabled%', $def->getArgument(1)); + $this->assertEquals('%form.type_extension.csrf.enabled%', $def->getArgument(0)); $this->assertEquals('_csrf', $container->getParameter('form.type_extension.csrf.field_name')); - $this->assertEquals('%form.type_extension.csrf.field_name%', $def->getArgument(2)); + $this->assertEquals('%form.type_extension.csrf.field_name%', $def->getArgument(1)); $this->assertEquals('s3cr3t', $container->getParameterBag()->resolveValue($container->findDefinition('form.csrf_provider')->getArgument(1))); } diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/Helper/Resources/Child/form_label.html.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/Helper/Resources/Child/field_label.html.php similarity index 100% rename from src/Symfony/Bundle/FrameworkBundle/Tests/Templating/Helper/Resources/Child/form_label.html.php rename to src/Symfony/Bundle/FrameworkBundle/Tests/Templating/Helper/Resources/Child/field_label.html.php diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/Helper/Resources/Parent/form_label.html.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/Helper/Resources/Parent/field_label.html.php similarity index 100% rename from src/Symfony/Bundle/FrameworkBundle/Tests/Templating/Helper/Resources/Parent/form_label.html.php rename to src/Symfony/Bundle/FrameworkBundle/Tests/Templating/Helper/Resources/Parent/field_label.html.php diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/Helper/Resources/Parent/input.html.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Templating/Helper/Resources/Parent/field_widget.html.php similarity index 100% rename from src/Symfony/Bundle/FrameworkBundle/Tests/Templating/Helper/Resources/Parent/input.html.php rename to src/Symfony/Bundle/FrameworkBundle/Tests/Templating/Helper/Resources/Parent/field_widget.html.php diff --git a/src/Symfony/Component/Form/DataTransformerInterface.php b/src/Symfony/Component/Form/DataTransformerInterface.php index 2f1543e91b58a..add2ce3d9bc97 100644 --- a/src/Symfony/Component/Form/DataTransformerInterface.php +++ b/src/Symfony/Component/Form/DataTransformerInterface.php @@ -24,7 +24,7 @@ interface DataTransformerInterface * This method is called on two occasions inside a form field: * * 1. When the form field is initialized with the data attached from the datasource (object or array). - * 2. When data from a request is bound using {@link Form::bind()} to transform the new input data + * 2. When data from a request is bound using {@link Field::bind()} to transform the new input data * back into the renderable format. For example if you have a date field and bind '2009-10-10' onto * it you might accept this value because its easily parsed, but the transformer still writes back * "2009/10/10" onto the form field (for further displaying or other purposes). @@ -52,7 +52,7 @@ function transform($value); * Transforms a value from the transformed representation to its original * representation. * - * This method is called when {@link Form::bind()} is called to transform the requests tainted data + * This method is called when {@link Field::bind()} is called to transform the requests tainted data * into an acceptable format for your data processing/model layer. * * This method must be able to deal with empty values. Usually this will diff --git a/src/Symfony/Component/Form/Exception/DanglingFieldException.php b/src/Symfony/Component/Form/Exception/DanglingFieldException.php new file mode 100644 index 0000000000000..29d10be36bbdd --- /dev/null +++ b/src/Symfony/Component/Form/Exception/DanglingFieldException.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Exception; + +/** + * Thrown when a field is expected to be added to a form but is not + * + * @author Bernhard Schussek + */ +class DanglingFieldException extends FormException +{ +} diff --git a/src/Symfony/Component/Form/Exception/FieldDefinitionException.php b/src/Symfony/Component/Form/Exception/FieldDefinitionException.php new file mode 100644 index 0000000000000..6cd904ad4a1b9 --- /dev/null +++ b/src/Symfony/Component/Form/Exception/FieldDefinitionException.php @@ -0,0 +1,16 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Exception; + +class FieldDefinitionException extends FormException +{ +} diff --git a/src/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php b/src/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php index 1a8ba2d1be6a2..1ed495248bb61 100644 --- a/src/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php +++ b/src/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php @@ -44,8 +44,8 @@ public function buildForm(FormBuilder $builder, array $options) } if ($options['expanded']) { - $this->addSubForms($builder, $options['choice_list']->getPreferredViews(), $options); - $this->addSubForms($builder, $options['choice_list']->getRemainingViews(), $options); + $this->addSubFields($builder, $options['choice_list']->getPreferredViews(), $options); + $this->addSubFields($builder, $options['choice_list']->getRemainingViews(), $options); } // empty value @@ -182,7 +182,7 @@ public function getDefaultOptions() */ public function getParent(array $options) { - return 'field'; + return isset($options['expanded']) && $options['expanded'] ? 'form' : 'field'; } /** @@ -200,12 +200,12 @@ public function getName() * @param array $choiceViews The choice view objects. * @param array $options The build options. */ - private function addSubForms(FormBuilder $builder, array $choiceViews, array $options) + private function addSubFields(FormBuilder $builder, array $choiceViews, array $options) { foreach ($choiceViews as $i => $choiceView) { if (is_array($choiceView)) { // Flatten groups - $this->addSubForms($builder, $choiceView, $options); + $this->addSubFields($builder, $choiceView, $options); } else { $choiceOpts = array( 'value' => $choiceView->getValue(), diff --git a/src/Symfony/Component/Form/Extension/Core/Type/DateTimeType.php b/src/Symfony/Component/Form/Extension/Core/Type/DateTimeType.php index f428054edee74..6eba72d4f6b6d 100644 --- a/src/Symfony/Component/Form/Extension/Core/Type/DateTimeType.php +++ b/src/Symfony/Component/Form/Extension/Core/Type/DateTimeType.php @@ -153,7 +153,7 @@ public function getDefaultOptions() 'widget' => null, // This will overwrite "empty_value" child options 'empty_value' => null, - // If initialized with a \DateTime object, FormType initializes + // If initialized with a \DateTime object, FieldType initializes // this option to "\DateTime". Since the internal, normalized // representation is not \DateTime, but an array, we need to unset // this option. @@ -200,7 +200,7 @@ public function getAllowedOptionValues() */ public function getParent(array $options) { - return 'field'; + return isset($options['widget']) && 'single_text' === $options['widget'] ? 'field' : 'form'; } /** diff --git a/src/Symfony/Component/Form/Extension/Core/Type/DateType.php b/src/Symfony/Component/Form/Extension/Core/Type/DateType.php index c506714c7ffd3..e58d0d93408d4 100644 --- a/src/Symfony/Component/Form/Extension/Core/Type/DateType.php +++ b/src/Symfony/Component/Form/Extension/Core/Type/DateType.php @@ -177,7 +177,7 @@ public function getDefaultOptions() // them like immutable value objects 'by_reference' => false, 'error_bubbling' => false, - // If initialized with a \DateTime object, FormType initializes + // If initialized with a \DateTime object, FieldType initializes // this option to "\DateTime". Since the internal, normalized // representation is not \DateTime, but an array, we need to unset // this option. @@ -210,7 +210,7 @@ public function getAllowedOptionValues() */ public function getParent(array $options) { - return 'field'; + return isset($options['widget']) && 'single_text' === $options['widget'] ? 'field' : 'form'; } /** diff --git a/src/Symfony/Component/Form/Extension/Core/Type/FieldType.php b/src/Symfony/Component/Form/Extension/Core/Type/FieldType.php index 5af6791346c39..74c9cb0327b67 100644 --- a/src/Symfony/Component/Form/Extension/Core/Type/FieldType.php +++ b/src/Symfony/Component/Form/Extension/Core/Type/FieldType.php @@ -14,15 +14,179 @@ use Symfony\Component\Form\AbstractType; use Symfony\Component\EventDispatcher\EventDispatcher; -/** - * Deprecated. You should extend FormType instead. - * - * @author Bernhard Schussek - * - * @deprecated Deprecated since version 2.1, to be removed in 2.3. - */ class FieldType extends AbstractType { + /** + * {@inheritdoc} + */ + public function buildForm(FormBuilder $builder, array $options) + { + if (null === $options['property_path']) { + $options['property_path'] = $builder->getName(); + } + + if (false === $options['property_path'] || '' === $options['property_path']) { + $options['property_path'] = null; + } else { + $options['property_path'] = new PropertyPath($options['property_path']); + } + if (!is_array($options['attr'])) { + throw new FormException('The "attr" option must be "array".'); + } + + $builder + ->setRequired($options['required']) + ->setDisabled($options['disabled']) + ->setErrorBubbling($options['error_bubbling']) + ->setEmptyData($options['empty_data']) + ->setAttribute('read_only', $options['read_only']) + ->setAttribute('by_reference', $options['by_reference']) + ->setAttribute('property_path', $options['property_path']) + ->setAttribute('error_mapping', $options['error_mapping']) + ->setAttribute('max_length', $options['max_length']) + ->setAttribute('pattern', $options['pattern']) + ->setAttribute('label', $options['label'] ?: $this->humanize($builder->getName())) + ->setAttribute('attr', $options['attr'] ?: array()) + ->setAttribute('invalid_message', $options['invalid_message']) + ->setAttribute('invalid_message_parameters', $options['invalid_message_parameters']) + ->setAttribute('translation_domain', $options['translation_domain']) + ->setData($options['data']) + ->addEventSubscriber(new ValidationListener()) + ; + + if ($options['trim']) { + $builder->addEventSubscriber(new TrimListener()); + } + } + + /** + * {@inheritdoc} + */ + public function buildView(FormView $view, FormInterface $form) + { + $name = $form->getName(); + $readOnly = $form->getAttribute('read_only'); + + if ($view->hasParent()) { + if ('' === $name) { + throw new FormException('Form node with empty name can be used only as root form node.'); + } + + if ('' !== ($parentFullName = $view->getParent()->get('full_name'))) { + $id = sprintf('%s_%s', $view->getParent()->get('id'), $name); + $fullName = sprintf('%s[%s]', $parentFullName, $name); + } else { + $id = $name; + $fullName = $name; + } + + // Complex fields are read-only if themselves or their parent is. + $readOnly = $readOnly || $view->getParent()->get('read_only'); + } else { + $id = $name; + $fullName = $name; + + // Strip leading underscores and digits. These are allowed in + // form names, but not in HTML4 ID attributes. + // http://www.w3.org/TR/html401/struct/global.html#adef-id + $id = ltrim($id, '_0123456789'); + } + + $types = array(); + foreach ($form->getTypes() as $type) { + $types[] = $type->getName(); + } + + $view + ->set('form', $view) + ->set('id', $id) + ->set('name', $name) + ->set('full_name', $fullName) + ->set('read_only', $readOnly) + ->set('errors', $form->getErrors()) + ->set('value', $form->getClientData()) + ->set('disabled', $form->isDisabled()) + ->set('required', $form->isRequired()) + ->set('max_length', $form->getAttribute('max_length')) + ->set('pattern', $form->getAttribute('pattern')) + ->set('size', null) + ->set('label', $form->getAttribute('label')) + ->set('multipart', false) + ->set('attr', $form->getAttribute('attr')) + ->set('types', $types) + ->set('translation_domain', $form->getAttribute('translation_domain')) + ; + } + + /** + * {@inheritdoc} + */ + public function getDefaultOptions() + { + // Derive "data_class" option from passed "data" object + $dataClass = function (Options $options) { + if (is_object($options['data'])) { + return get_class($options['data']); + } + + return null; + }; + + // Derive "empty_data" closure from "data_class" option + $emptyData = function (Options $options) { + $class = $options['data_class']; + + if (null !== $class) { + return function (FormInterface $form) use ($class) { + if ($form->isEmpty() && !$form->isRequired()) { + return null; + } + + return new $class(); + }; + } + + return ''; + }; + + return array( + 'data' => null, + 'data_class' => $dataClass, + 'empty_data' => $emptyData, + 'trim' => true, + 'required' => true, + 'read_only' => false, + 'disabled' => false, + 'max_length' => null, + 'pattern' => null, + 'property_path' => null, + 'by_reference' => true, + 'error_bubbling' => false, + 'error_mapping' => array(), + 'label' => null, + 'attr' => array(), + 'invalid_message' => 'This value is not valid', + 'invalid_message_parameters' => array(), + 'translation_domain' => 'messages', + ); + } + + /** + * {@inheritdoc} + */ + public function createBuilder($name, FormFactoryInterface $factory, array $options) + { + return new FormBuilder($name, $factory, new EventDispatcher(), $options['data_class']); + } + + /** + * {@inheritdoc} + */ + public function getParent(array $options) + { + return null; + } + /** * {@inheritdoc} */ @@ -30,4 +194,9 @@ public function getName() { return 'field'; } + + private function humanize($text) + { + return ucfirst(trim(strtolower(preg_replace('/[_\s]+/', ' ', $text)))); + } } diff --git a/src/Symfony/Component/Form/Extension/Core/Type/FileType.php b/src/Symfony/Component/Form/Extension/Core/Type/FileType.php index 468c65a0f359a..30f37203b286d 100644 --- a/src/Symfony/Component/Form/Extension/Core/Type/FileType.php +++ b/src/Symfony/Component/Form/Extension/Core/Type/FileType.php @@ -23,24 +23,12 @@ class FileType extends AbstractType public function buildView(FormView $view, FormInterface $form) { $view + ->set('multipart', true) ->set('type', 'file') ->set('value', '') ; } - /** - * {@inheritdoc} - */ - public function buildViewBottomUp(FormView $view, FormInterface $form) - { - $view - ->set('multipart', true) - ; - } - - /** - * {@inheritdoc} - */ public function getParent(array $options) { return 'field'; diff --git a/src/Symfony/Component/Form/Extension/Core/Type/FormType.php b/src/Symfony/Component/Form/Extension/Core/Type/FormType.php index ec5870acff7d3..36e0c0c63bb11 100644 --- a/src/Symfony/Component/Form/Extension/Core/Type/FormType.php +++ b/src/Symfony/Component/Form/Extension/Core/Type/FormType.php @@ -13,16 +13,10 @@ use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\Options; -use Symfony\Component\Form\Util\PropertyPath; use Symfony\Component\Form\FormBuilder; use Symfony\Component\Form\FormInterface; -use Symfony\Component\Form\FormFactoryInterface; use Symfony\Component\Form\FormView; -use Symfony\Component\Form\Extension\Core\EventListener\TrimListener; -use Symfony\Component\Form\Extension\Core\EventListener\ValidationListener; use Symfony\Component\Form\Extension\Core\DataMapper\PropertyPathMapper; -use Symfony\Component\EventDispatcher\EventDispatcher; -use Symfony\Component\Form\Exception\FormException; class FormType extends AbstractType { @@ -31,102 +25,9 @@ class FormType extends AbstractType */ public function buildForm(FormBuilder $builder, array $options) { - if (null === $options['property_path']) { - $options['property_path'] = $builder->getName(); - } - - if (false === $options['property_path'] || '' === $options['property_path']) { - $options['property_path'] = null; - } else { - $options['property_path'] = new PropertyPath($options['property_path']); - } - if (!is_array($options['attr'])) { - throw new FormException('The "attr" option must be "array".'); - } - $builder - ->setRequired($options['required']) - ->setDisabled($options['disabled']) - ->setErrorBubbling($options['error_bubbling']) - ->setEmptyData($options['empty_data']) - ->setAttribute('read_only', $options['read_only']) - ->setAttribute('by_reference', $options['by_reference']) - ->setAttribute('property_path', $options['property_path']) - ->setAttribute('error_mapping', $options['error_mapping']) - ->setAttribute('max_length', $options['max_length']) - ->setAttribute('pattern', $options['pattern']) - ->setAttribute('label', $options['label'] ?: $this->humanize($builder->getName())) - ->setAttribute('attr', $options['attr'] ?: array()) - ->setAttribute('invalid_message', $options['invalid_message']) - ->setAttribute('invalid_message_parameters', $options['invalid_message_parameters']) - ->setAttribute('translation_domain', $options['translation_domain']) ->setAttribute('virtual', $options['virtual']) - ->setData($options['data']) ->setDataMapper(new PropertyPathMapper($options['data_class'])) - ->addEventSubscriber(new ValidationListener()) - ; - - if ($options['trim']) { - $builder->addEventSubscriber(new TrimListener()); - } - } - - /** - * {@inheritdoc} - */ - public function buildView(FormView $view, FormInterface $form) - { - $name = $form->getName(); - $readOnly = $form->getAttribute('read_only'); - - if ($view->hasParent()) { - if ('' === $name) { - throw new FormException('Form node with empty name can be used only as root form node.'); - } - - if ('' !== ($parentFullName = $view->getParent()->get('full_name'))) { - $id = sprintf('%s_%s', $view->getParent()->get('id'), $name); - $fullName = sprintf('%s[%s]', $parentFullName, $name); - } else { - $id = $name; - $fullName = $name; - } - - // Complex fields are read-only if themselves or their parent is. - $readOnly = $readOnly || $view->getParent()->get('read_only'); - } else { - $id = $name; - $fullName = $name; - - // Strip leading underscores and digits. These are allowed in - // form names, but not in HTML4 ID attributes. - // http://www.w3.org/TR/html401/struct/global.html#adef-id - $id = ltrim($id, '_0123456789'); - } - - $types = array(); - foreach ($form->getTypes() as $type) { - $types[] = $type->getName(); - } - - $view - ->set('form', $view) - ->set('id', $id) - ->set('name', $name) - ->set('full_name', $fullName) - ->set('read_only', $readOnly) - ->set('errors', $form->getErrors()) - ->set('value', $form->getClientData()) - ->set('disabled', $form->isDisabled()) - ->set('required', $form->isRequired()) - ->set('max_length', $form->getAttribute('max_length')) - ->set('pattern', $form->getAttribute('pattern')) - ->set('size', null) - ->set('label', $form->getAttribute('label')) - ->set('multipart', false) - ->set('attr', $form->getAttribute('attr')) - ->set('types', $types) - ->set('translation_domain', $form->getAttribute('translation_domain')) ; } @@ -152,75 +53,29 @@ public function buildViewBottomUp(FormView $view, FormInterface $form) */ public function getDefaultOptions() { - // Derive "data_class" option from passed "data" object - $dataClass = function (Options $options) { - if (is_object($options['data'])) { - return get_class($options['data']); + $emptyData = function (Options $options, $currentValue) { + if (empty($options['data_class'])) { + return array(); } - return null; + return $currentValue; }; - // Derive "empty_data" closure from "data_class" option - $emptyData = function (Options $options) { - $class = $options['data_class']; - - if (null !== $class) { - return function (FormInterface $form) use ($class) { - if ($form->isEmpty() && !$form->isRequired()) { - return null; - } - - return new $class(); - }; - } - - return function (FormInterface $form) { - if ($form->hasChildren()) { - return array(); - } - - return ''; - }; - }; - return array( - 'data' => null, - 'data_class' => $dataClass, 'empty_data' => $emptyData, - 'trim' => true, - 'required' => true, - 'read_only' => false, - 'disabled' => false, - 'max_length' => null, - 'pattern' => null, - 'property_path' => null, - 'by_reference' => true, - 'error_bubbling' => false, - 'error_mapping' => array(), - 'label' => null, - 'attr' => array(), 'virtual' => false, - 'invalid_message' => 'This value is not valid.', - 'invalid_message_parameters' => array(), - 'translation_domain' => 'messages', + // Errors in forms bubble by default, so that form errors will + // end up as global errors in the root form + 'error_bubbling' => true, ); } - /** - * {@inheritdoc} - */ - public function createBuilder($name, FormFactoryInterface $factory, array $options) - { - return new FormBuilder($name, $factory, new EventDispatcher(), $options['data_class']); - } - /** * {@inheritdoc} */ public function getParent(array $options) { - return null; + return 'field'; } /** @@ -230,9 +85,4 @@ public function getName() { return 'form'; } - - private function humanize($text) - { - return ucfirst(trim(strtolower(preg_replace('/[_\s]+/', ' ', $text)))); - } } diff --git a/src/Symfony/Component/Form/Extension/Core/Type/TimeType.php b/src/Symfony/Component/Form/Extension/Core/Type/TimeType.php index 546220d0ab318..3f7258bb82e70 100644 --- a/src/Symfony/Component/Form/Extension/Core/Type/TimeType.php +++ b/src/Symfony/Component/Form/Extension/Core/Type/TimeType.php @@ -150,7 +150,7 @@ public function getDefaultOptions() // them like immutable value objects 'by_reference' => false, 'error_bubbling' => false, - // If initialized with a \DateTime object, FormType initializes + // If initialized with a \DateTime object, FieldType initializes // this option to "\DateTime". Since the internal, normalized // representation is not \DateTime, but an array, we need to unset // this option. @@ -183,7 +183,7 @@ public function getAllowedOptionValues() */ public function getParent(array $options) { - return 'field'; + return isset($options['widget']) && 'single_text' === $options['widget'] ? 'field' : 'form'; } /** diff --git a/src/Symfony/Component/Form/Extension/Csrf/CsrfExtension.php b/src/Symfony/Component/Form/Extension/Csrf/CsrfExtension.php index 5330bbda45d8f..42505e2b762bc 100644 --- a/src/Symfony/Component/Form/Extension/Csrf/CsrfExtension.php +++ b/src/Symfony/Component/Form/Extension/Csrf/CsrfExtension.php @@ -32,13 +32,27 @@ public function __construct(CsrfProviderInterface $csrfProvider) $this->csrfProvider = $csrfProvider; } + /** + * {@inheritDoc} + */ + protected function loadTypes() + { + return array( + new Type\CsrfType($this->csrfProvider), + ); + } + /** * {@inheritDoc} */ protected function loadTypeExtensions() { return array( - new Type\FormTypeCsrfExtension($this->csrfProvider), + new Type\ChoiceTypeCsrfExtension(), + new Type\DateTypeCsrfExtension(), + new Type\FormTypeCsrfExtension(), + new Type\RepeatedTypeCsrfExtension(), + new Type\TimeTypeCsrfExtension(), ); } } diff --git a/src/Symfony/Component/Form/Extension/Csrf/EventListener/CsrfValidationListener.php b/src/Symfony/Component/Form/Extension/Csrf/EventListener/CsrfValidationListener.php index dd74d87b2eaea..3de52ab867a59 100644 --- a/src/Symfony/Component/Form/Extension/Csrf/EventListener/CsrfValidationListener.php +++ b/src/Symfony/Component/Form/Extension/Csrf/EventListener/CsrfValidationListener.php @@ -14,7 +14,7 @@ use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symfony\Component\Form\FormEvents; use Symfony\Component\Form\FormError; -use Symfony\Component\Form\Event\FilterDataEvent; +use Symfony\Component\Form\Event\DataEvent; use Symfony\Component\Form\Extension\Csrf\CsrfProvider\CsrfProviderInterface; /** @@ -22,12 +22,6 @@ */ class CsrfValidationListener implements EventSubscriberInterface { - /** - * The name of the CSRF field - * @var string - */ - private $fieldName; - /** * The provider for generating and validating CSRF tokens * @var CsrfProviderInterface @@ -51,26 +45,24 @@ static public function getSubscribedEvents() ); } - public function __construct($fieldName, CsrfProviderInterface $csrfProvider, $intention) + public function __construct(CsrfProviderInterface $csrfProvider, $intention) { - $this->fieldName = $fieldName; $this->csrfProvider = $csrfProvider; $this->intention = $intention; } - public function onBindClientData(FilterDataEvent $event) + public function onBindClientData(DataEvent $event) { $form = $event->getForm(); $data = $event->getData(); - if ($form->isRoot() && $form->hasChildren() && isset($data[$this->fieldName])) { - if (!$this->csrfProvider->isCsrfTokenValid($this->intention, $data[$this->fieldName])) { - $form->addError(new FormError('The CSRF token is invalid. Please try to resubmit the form')); - } + if ((!$form->hasParent() || $form->getParent()->isRoot()) + && !$this->csrfProvider->isCsrfTokenValid($this->intention, $data)) { + $form->addError(new FormError('The CSRF token is invalid. Please try to resubmit the form')); - unset($data[$this->fieldName]); + // If the session timed out, the token is invalid now. + // Regenerate the token so that a resubmission is possible. + $event->setData($this->csrfProvider->generateCsrfToken($this->intention)); } - - $event->setData($data); } } diff --git a/src/Symfony/Component/Form/Extension/Csrf/EventListener/EnsureCsrfFieldListener.php b/src/Symfony/Component/Form/Extension/Csrf/EventListener/EnsureCsrfFieldListener.php new file mode 100644 index 0000000000000..480d67c323305 --- /dev/null +++ b/src/Symfony/Component/Form/Extension/Csrf/EventListener/EnsureCsrfFieldListener.php @@ -0,0 +1,66 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Extension\Csrf\EventListener; + +use Symfony\Component\Form\Event\DataEvent; +use Symfony\Component\Form\Extension\Csrf\CsrfProvider\CsrfProviderInterface; +use Symfony\Component\Form\FormFactoryInterface; + +/** + * Ensures the CSRF field. + * + * @author Bulat Shakirzyanov + * @author Kris Wallsmith + */ +class EnsureCsrfFieldListener +{ + private $factory; + private $name; + private $intention; + private $provider; + + /** + * Constructor. + * + * @param FormFactoryInterface $factory The form factory + * @param string $name A name for the CSRF field + * @param string $intention The intention string + * @param CsrfProviderInterface $provider The CSRF provider + */ + public function __construct(FormFactoryInterface $factory, $name, $intention = null, CsrfProviderInterface $provider = null) + { + $this->factory = $factory; + $this->name = $name; + $this->intention = $intention; + $this->provider = $provider; + } + + /** + * Ensures a root form has a CSRF field. + * + * This method should be connected to both form.pre_set_data and form.pre_bind. + */ + public function ensureCsrfField(DataEvent $event) + { + $form = $event->getForm(); + + $options = array(); + if ($this->intention) { + $options['intention'] = $this->intention; + } + if ($this->provider) { + $options['csrf_provider'] = $this->provider; + } + + $form->add($this->factory->createNamed('csrf', $this->name, null, $options)); + } +} diff --git a/src/Symfony/Component/Form/Extension/Csrf/Type/ChoiceTypeCsrfExtension.php b/src/Symfony/Component/Form/Extension/Csrf/Type/ChoiceTypeCsrfExtension.php new file mode 100644 index 0000000000000..dc1e6db15bd1f --- /dev/null +++ b/src/Symfony/Component/Form/Extension/Csrf/Type/ChoiceTypeCsrfExtension.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Extension\Csrf\Type; + +use Symfony\Component\Form\AbstractTypeExtension; + +class ChoiceTypeCsrfExtension extends AbstractTypeExtension +{ + public function getDefaultOptions() + { + return array('csrf_protection' => false); + } + + public function getExtendedType() + { + return 'choice'; + } +} diff --git a/src/Symfony/Component/Form/Extension/Csrf/Type/CsrfType.php b/src/Symfony/Component/Form/Extension/Csrf/Type/CsrfType.php new file mode 100644 index 0000000000000..97c69d8a5ab1f --- /dev/null +++ b/src/Symfony/Component/Form/Extension/Csrf/Type/CsrfType.php @@ -0,0 +1,83 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Extension\Csrf\Type; + + +use Symfony\Component\Form\AbstractType; +use Symfony\Component\Form\FormBuilder; +use Symfony\Component\Form\Extension\Csrf\EventListener\CsrfValidationListener; +use Symfony\Component\Form\Extension\Csrf\CsrfProvider\CsrfProviderInterface; + +class CsrfType extends AbstractType +{ + private $csrfProvider; + + /** + * Constructor. + * + * @param CsrfProviderInterface $csrfProvider The provider to use to generate the token + */ + public function __construct(CsrfProviderInterface $csrfProvider) + { + $this->csrfProvider = $csrfProvider; + } + + /** + * Builds the CSRF field. + * + * A validator is added to check the token value when the CSRF field is added to + * a root form + * + * @param FormBuilder $builder The form builder + * @param array $options The options + */ + public function buildForm(FormBuilder $builder, array $options) + { + $csrfProvider = $options['csrf_provider']; + $intention = $options['intention']; + + $builder + ->setData($csrfProvider->generateCsrfToken($intention)) + ->addEventSubscriber(new CsrfValidationListener($csrfProvider, $intention)) + ; + } + + /** + * {@inheritDoc} + */ + public function getDefaultOptions() + { + return array( + 'csrf_provider' => $this->csrfProvider, + 'intention' => null, + 'property_path' => false, + ); + } + + /** + * {@inheritDoc} + */ + public function getParent(array $options) + { + return 'hidden'; + } + + /** + * Returns the name of this form. + * + * @return string 'csrf' + */ + public function getName() + { + return 'csrf'; + } +} diff --git a/src/Symfony/Component/Form/Extension/Csrf/Type/DateTypeCsrfExtension.php b/src/Symfony/Component/Form/Extension/Csrf/Type/DateTypeCsrfExtension.php new file mode 100644 index 0000000000000..dc54e94ddf6b4 --- /dev/null +++ b/src/Symfony/Component/Form/Extension/Csrf/Type/DateTypeCsrfExtension.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Extension\Csrf\Type; + +use Symfony\Component\Form\AbstractTypeExtension; + +class DateTypeCsrfExtension extends AbstractTypeExtension +{ + public function getDefaultOptions() + { + return array('csrf_protection' => false); + } + + public function getExtendedType() + { + return 'date'; + } +} diff --git a/src/Symfony/Component/Form/Extension/Csrf/Type/FormTypeCsrfExtension.php b/src/Symfony/Component/Form/Extension/Csrf/Type/FormTypeCsrfExtension.php index f0c3d8e312dcc..c248c3182e489 100644 --- a/src/Symfony/Component/Form/Extension/Csrf/Type/FormTypeCsrfExtension.php +++ b/src/Symfony/Component/Form/Extension/Csrf/Type/FormTypeCsrfExtension.php @@ -12,26 +12,20 @@ namespace Symfony\Component\Form\Extension\Csrf\Type; use Symfony\Component\Form\AbstractTypeExtension; -use Symfony\Component\Form\Extension\Csrf\CsrfProvider\CsrfProviderInterface; -use Symfony\Component\Form\Extension\Csrf\EventListener\CsrfValidationListener; +use Symfony\Component\Form\Extension\Csrf\EventListener\EnsureCsrfFieldListener; use Symfony\Component\Form\FormBuilder; use Symfony\Component\Form\FormView; use Symfony\Component\Form\FormInterface; -/** - * @author Bernhard Schussek - */ class FormTypeCsrfExtension extends AbstractTypeExtension { - private $defaultCsrfProvider; - private $defaultEnabled; - private $defaultFieldName; + private $enabled; + private $fieldName; - public function __construct(CsrfProviderInterface $defaultCsrfProvider, $defaultEnabled = true, $defaultFieldName = '_token') + public function __construct($enabled = true, $fieldName = '_token') { - $this->defaultCsrfProvider = $defaultCsrfProvider; - $this->defaultEnabled = $defaultEnabled; - $this->defaultFieldName = $defaultFieldName; + $this->enabled = $enabled; + $this->fieldName = $fieldName; } /** @@ -46,35 +40,35 @@ public function buildForm(FormBuilder $builder, array $options) return; } + $listener = new EnsureCsrfFieldListener( + $builder->getFormFactory(), + $options['csrf_field_name'], + $options['intention'], + $options['csrf_provider'] + ); + // use a low priority so higher priority listeners don't remove the field $builder ->setAttribute('csrf_field_name', $options['csrf_field_name']) - ->setAttribute('csrf_provider', $options['csrf_provider']) - ->setAttribute('csrf_intention', $options['intention']) - ->setAttribute('csrf_factory', $builder->getFormFactory()) - ->addEventSubscriber(new CsrfValidationListener($options['csrf_field_name'], $options['csrf_provider'], $options['intention'])) + ->addEventListener(FormEvents::PRE_SET_DATA, array($listener, 'ensureCsrfField'), -10) + ->addEventListener(FormEvents::PRE_BIND, array($listener, 'ensureCsrfField'), -10) ; } /** - * Adds a CSRF field to the root form view. + * Removes CSRF fields from all the form views except the root one. * * @param FormView $view The form view * @param FormInterface $form The form */ - public function buildView(FormView $view, FormInterface $form) + public function buildViewBottomUp(FormView $view, FormInterface $form) { - if ($form->isRoot() && $form->hasChildren() && $form->hasAttribute('csrf_field_name')) { + if ($view->hasParent() && $form->hasAttribute('csrf_field_name')) { $name = $form->getAttribute('csrf_field_name'); - $csrfProvider = $form->getAttribute('csrf_provider'); - $intention = $form->getAttribute('csrf_intention'); - $factory = $form->getAttribute('csrf_factory'); - $data = $csrfProvider->generateCsrfToken($intention); - $csrfForm = $factory->createNamed('hidden', $name, $data, array( - 'property_path' => false, - )); - $view->addChild($csrfForm->createView($view)); + if (isset($view[$name])) { + unset($view[$name]); + } } } @@ -84,9 +78,9 @@ public function buildView(FormView $view, FormInterface $form) public function getDefaultOptions() { return array( - 'csrf_protection' => $this->defaultEnabled, - 'csrf_field_name' => $this->defaultFieldName, - 'csrf_provider' => $this->defaultCsrfProvider, + 'csrf_protection' => $this->enabled, + 'csrf_field_name' => $this->fieldName, + 'csrf_provider' => null, 'intention' => 'unknown', ); } diff --git a/src/Symfony/Component/Form/Extension/Csrf/Type/RepeatedTypeCsrfExtension.php b/src/Symfony/Component/Form/Extension/Csrf/Type/RepeatedTypeCsrfExtension.php new file mode 100644 index 0000000000000..1115ea4d69ce2 --- /dev/null +++ b/src/Symfony/Component/Form/Extension/Csrf/Type/RepeatedTypeCsrfExtension.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Extension\Csrf\Type; + +use Symfony\Component\Form\AbstractTypeExtension; + +class RepeatedTypeCsrfExtension extends AbstractTypeExtension +{ + public function getDefaultOptions() + { + return array('csrf_protection' => false); + } + + public function getExtendedType() + { + return 'repeated'; + } +} diff --git a/src/Symfony/Component/Form/Extension/Csrf/Type/TimeTypeCsrfExtension.php b/src/Symfony/Component/Form/Extension/Csrf/Type/TimeTypeCsrfExtension.php new file mode 100644 index 0000000000000..dbd7c0d2dfbcb --- /dev/null +++ b/src/Symfony/Component/Form/Extension/Csrf/Type/TimeTypeCsrfExtension.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Extension\Csrf\Type; + +use Symfony\Component\Form\AbstractTypeExtension; + +class TimeTypeCsrfExtension extends AbstractTypeExtension +{ + public function getDefaultOptions() + { + return array('csrf_protection' => false); + } + + public function getExtendedType() + { + return 'time'; + } +} diff --git a/src/Symfony/Component/Form/Extension/Validator/Type/FormTypeValidatorExtension.php b/src/Symfony/Component/Form/Extension/Validator/Type/FieldTypeValidatorExtension.php similarity index 95% rename from src/Symfony/Component/Form/Extension/Validator/Type/FormTypeValidatorExtension.php rename to src/Symfony/Component/Form/Extension/Validator/Type/FieldTypeValidatorExtension.php index 77754669f3606..8d0db3f4f6089 100644 --- a/src/Symfony/Component/Form/Extension/Validator/Type/FormTypeValidatorExtension.php +++ b/src/Symfony/Component/Form/Extension/Validator/Type/FieldTypeValidatorExtension.php @@ -19,7 +19,7 @@ /** * @author Bernhard Schussek */ -class FormTypeValidatorExtension extends AbstractTypeExtension +class FieldTypeValidatorExtension extends AbstractTypeExtension { private $validator; @@ -57,6 +57,6 @@ public function getDefaultOptions() public function getExtendedType() { - return 'form'; + return 'field'; } } diff --git a/src/Symfony/Component/Form/Extension/Validator/ValidatorExtension.php b/src/Symfony/Component/Form/Extension/Validator/ValidatorExtension.php index b4efa1679acdd..94b6ea699d841 100644 --- a/src/Symfony/Component/Form/Extension/Validator/ValidatorExtension.php +++ b/src/Symfony/Component/Form/Extension/Validator/ValidatorExtension.php @@ -38,7 +38,7 @@ public function loadTypeGuesser() protected function loadTypeExtensions() { return array( - new Type\FormTypeValidatorExtension($this->validator), + new Type\FieldTypeValidatorExtension($this->validator), ); } } diff --git a/src/Symfony/Component/Form/Form.php b/src/Symfony/Component/Form/Form.php index 5669e0ead4956..8f9e7b6de2403 100644 --- a/src/Symfony/Component/Form/Form.php +++ b/src/Symfony/Component/Form/Form.php @@ -191,7 +191,7 @@ public function __construct($name, EventDispatcherInterface $dispatcher, array $types = array(), array $clientTransformers = array(), array $normTransformers = array(), DataMapperInterface $dataMapper = null, array $validators = array(), - $required = false, $disabled = false, $errorBubbling = null, + $required = false, $disabled = false, $errorBubbling = false, $emptyData = null, array $attributes = array()) { $name = (string) $name; @@ -225,10 +225,7 @@ public function __construct($name, EventDispatcherInterface $dispatcher, $this->validators = $validators; $this->required = (Boolean) $required; $this->disabled = (Boolean) $disabled; - // NULL is the default meaning: - // bubble up if the form has children (complex forms) - // don't bubble up if the form has no children (primitive fields) - $this->errorBubbling = null === $errorBubbling ? null : (Boolean) $errorBubbling; + $this->errorBubbling = (Boolean) $errorBubbling; $this->emptyData = $emptyData; $this->attributes = $attributes; @@ -315,9 +312,9 @@ public function setParent(FormInterface $parent = null) } /** - * Returns the parent form. + * Returns the parent field. * - * @return FormInterface The parent form + * @return FormInterface The parent field */ public function getParent() { @@ -345,7 +342,7 @@ public function getRoot() } /** - * Returns whether the form is the root of the form tree. + * Returns whether the field is the root of the form tree. * * @return Boolean */ @@ -377,7 +374,7 @@ public function getAttribute($name) } /** - * Updates the form with default data. + * Updates the field with default data. * * @param array $appData The data formatted as expected for the underlying object * @@ -411,7 +408,7 @@ public function setData($appData) $this->clientData = $clientData; $this->synchronized = true; - if (count($this->children) > 0 && $this->dataMapper) { + if ($this->dataMapper) { // Update child forms from the data $this->dataMapper->mapDataToForms($clientData, $this->children); } @@ -453,7 +450,7 @@ public function getExtraData() } /** - * Binds data to the form, transforms and validates it. + * Binds data to the field, transforms and validates it. * * @param string|array $clientData The data * @@ -629,11 +626,11 @@ public function bindRequest(Request $request) } /** - * Returns the normalized data of the form. + * Returns the normalized data of the field. * - * @return mixed When the form is not bound, the default data is returned. - * When the form is bound, the normalized bound data is - * returned if the form is valid, null otherwise. + * @return mixed When the field is not bound, the default data is returned. + * When the field is bound, the normalized bound data is + * returned if the field is valid, null otherwise. */ public function getNormData() { @@ -649,7 +646,7 @@ public function getNormData() */ public function addError(FormError $error) { - if ($this->parent && $this->getErrorBubbling()) { + if ($this->parent && $this->errorBubbling) { $this->parent->addError($error); } else { $this->errors[] = $error; @@ -665,11 +662,11 @@ public function addError(FormError $error) */ public function getErrorBubbling() { - return null === $this->errorBubbling ? $this->hasChildren() : $this->errorBubbling; + return $this->errorBubbling; } /** - * Returns whether the form is bound. + * Returns whether the field is bound. * * @return Boolean true if the form is bound to input values, false otherwise */ @@ -705,7 +702,7 @@ public function isEmpty() } /** - * Returns whether the form is valid. + * Returns whether the field is valid. * * @return Boolean */ @@ -738,8 +735,9 @@ public function isValid() public function hasErrors() { // Don't call isValid() here, as its semantics are slightly different - // Forms are not valid if their children are invalid, but - // hasErrors() returns only true if a form itself has errors + // Field groups are not valid if their children are invalid, but + // hasErrors() returns only true if a field/field group itself has + // errors return count($this->errors) > 0; } @@ -896,7 +894,7 @@ public function get($name) return $this->children[$name]; } - throw new \InvalidArgumentException(sprintf('Child "%s" does not exist.', $name)); + throw new \InvalidArgumentException(sprintf('Field "%s" does not exist.', $name)); } /** @@ -977,7 +975,7 @@ public function createView(FormView $parent = null) $parent = $this->parent->createView(); } - $view = new FormView($this->name); + $view = new FormView(); $view->setParent($parent); @@ -991,10 +989,14 @@ public function createView(FormView $parent = null) } } - foreach ($this->children as $child) { - $view->addChild($child->createView($view)); + $childViews = array(); + + foreach ($this->children as $key => $child) { + $childViews[$key] = $child->createView($view); } + $view->setChildren($childViews); + foreach ($types as $type) { $type->buildViewBottomUp($view, $this); diff --git a/src/Symfony/Component/Form/FormBuilder.php b/src/Symfony/Component/Form/FormBuilder.php index b8f7da16c65a8..208a518518b41 100644 --- a/src/Symfony/Component/Form/FormBuilder.php +++ b/src/Symfony/Component/Form/FormBuilder.php @@ -106,7 +106,7 @@ class FormBuilder * Whether added errors should bubble up to the parent * @var Boolean */ - private $errorBubbling; + private $errorBubbling = false; /** * Data used for the client data when no value is bound @@ -243,7 +243,7 @@ public function getRequired() */ public function setErrorBubbling($errorBubbling) { - $this->errorBubbling = null === $errorBubbling ? null : (Boolean) $errorBubbling; + $this->errorBubbling = (Boolean) $errorBubbling; return $this; } diff --git a/src/Symfony/Component/Form/FormInterface.php b/src/Symfony/Component/Form/FormInterface.php index f3ca837356436..d0d8a80a8d127 100644 --- a/src/Symfony/Component/Form/FormInterface.php +++ b/src/Symfony/Component/Form/FormInterface.php @@ -183,7 +183,7 @@ function isRequired(); * The content of a disabled form is displayed, but not allowed to be * modified. The validation of modified disabled forms should fail. * - * Forms whose parents are disabled are considered disabled regardless of + * Fields whose parents are disabled are considered disabled regardless of * their own state. * * @return Boolean diff --git a/src/Symfony/Component/Form/FormTypeGuesserChain.php b/src/Symfony/Component/Form/FormTypeGuesserChain.php index 1e9681db8a3cf..b58daa46502bb 100644 --- a/src/Symfony/Component/Form/FormTypeGuesserChain.php +++ b/src/Symfony/Component/Form/FormTypeGuesserChain.php @@ -97,7 +97,7 @@ public function guessPattern($class, $property) * @param \Closure $closure The closure to execute. Accepts a guesser * as argument and should return a Guess instance * - * @return Guess The guess with the highest confidence + * @return FieldFactoryGuess The guess with the highest confidence */ private function guess(\Closure $closure) { diff --git a/src/Symfony/Component/Form/FormView.php b/src/Symfony/Component/Form/FormView.php index 5d88346edc7f9..5c8ab13da12bb 100644 --- a/src/Symfony/Component/Form/FormView.php +++ b/src/Symfony/Component/Form/FormView.php @@ -11,17 +11,8 @@ namespace Symfony\Component\Form; -use ArrayAccess; -use IteratorAggregate; -use Countable; - -/** - * @author Bernhard Schussek - */ -class FormView implements ArrayAccess, IteratorAggregate, Countable +class FormView implements \ArrayAccess, \IteratorAggregate, \Countable { - private $name; - private $vars = array( 'value' => null, 'attr' => array(), @@ -42,16 +33,6 @@ class FormView implements ArrayAccess, IteratorAggregate, Countable */ private $rendered = false; - public function __construct($name) - { - $this->name = $name; - } - - public function getName() - { - return $this->name; - } - /** * @param string $name * @param mixed $value @@ -196,29 +177,15 @@ public function hasParent() } /** - * Adds a child view. + * Sets the children view. * - * @param FormView $child The child view to add. + * @param array $children The children as instances of FormView * * @return FormView The current view */ - public function addChild(FormView $child) + public function setChildren(array $children) { - $this->children[$child->getName()] = $child; - - return $this; - } - - /** - * Removes a child view. - * - * @param string $name The name of the removed child view. - * - * @return FormView The current view - */ - public function removeChild($name) - { - unset($this->children[$name]); + $this->children = $children; return $this; } @@ -255,18 +222,6 @@ public function hasChildren() return count($this->children) > 0; } - /** - * Returns whether this view has a given child. - * - * @param string $name The name of the child - * - * @return Boolean Whether the child with the given name exists - */ - public function hasChild($name) - { - return isset($this->children[$name]); - } - /** * Returns a child by name (implements \ArrayAccess). * diff --git a/src/Symfony/Component/Form/Tests/AbstractDivLayoutTest.php b/src/Symfony/Component/Form/Tests/AbstractDivLayoutTest.php index 7d1512f0037bd..d22113245a09d 100644 --- a/src/Symfony/Component/Form/Tests/AbstractDivLayoutTest.php +++ b/src/Symfony/Component/Form/Tests/AbstractDivLayoutTest.php @@ -202,7 +202,7 @@ public function testRestAndRepeatedWithRow() ); } - public function testRestAndRepeatedWithRowPerChild() + public function testRestAndRepeatedWithRowPerField() { $view = $this->factory->createNamedBuilder('form', 'name') ->add('first', 'text') @@ -230,7 +230,7 @@ public function testRestAndRepeatedWithRowPerChild() ); } - public function testRestAndRepeatedWithWidgetPerChild() + public function testRestAndRepeatedWithWidgetPerField() { $view = $this->factory->createNamedBuilder('form', 'name') ->add('first', 'text') @@ -348,10 +348,7 @@ public function testForm() public function testNestedFormError() { $form = $this->factory->createNamedBuilder('form', 'name') - ->add($this->factory - ->createNamedBuilder('form', 'child', null, array('error_bubbling' => false)) - ->add('grandChild', 'form') - ) + ->add('child', 'form', array('error_bubbling' => false)) ->getForm(); $form->get('child')->addError(new FormError('Error!')); @@ -366,31 +363,6 @@ public function testNestedFormError() ); } - public function testCsrf() - { - $this->csrfProvider->expects($this->any()) - ->method('generateCsrfToken') - ->will($this->returnValue('foo&bar')); - - $form = $this->factory->createNamedBuilder('form', 'name') - ->add($this->factory - // No CSRF protection on nested forms - ->createNamedBuilder('form', 'child') - ->add($this->factory->createNamedBuilder('text', 'grandchild')) - ) - ->getForm(); - - $this->assertWidgetMatchesXpath($form->createView(), array(), -'/div - [ - ./input[@type="hidden"][@id="name__token"][@value="foo&bar"] - /following-sibling::div - ] - [count(.//input[@type="hidden"])=1] -' - ); - } - public function testRepeated() { $form = $this->factory->createNamed('repeated', 'name', 'foobar', array( @@ -400,8 +372,7 @@ public function testRepeated() $this->assertWidgetMatchesXpath($form->createView(), array(), '/div [ - ./input[@type="hidden"][@id="name__token"] - /following-sibling::div + ./div [ ./label[@for="name_first"] /following-sibling::input[@type="text"][@id="name_first"] @@ -412,7 +383,7 @@ public function testRepeated() /following-sibling::input[@type="text"][@id="name_second"] ] ] - [count(.//input)=3] + [count(.//input)=2] ' ); } @@ -428,8 +399,7 @@ public function testRepeatedWithCustomOptions() $this->assertWidgetMatchesXpath($form->createView(), array(), '/div [ - ./input[@type="hidden"][@id="name__token"] - /following-sibling::div + ./div [ ./label[@for="name_first"][.="[trans]Test[/trans]"] /following-sibling::input[@type="text"][@id="name_first"][@required="required"] @@ -440,7 +410,7 @@ public function testRepeatedWithCustomOptions() /following-sibling::input[@type="text"][@id="name_second"][@required="required"] ] ] - [count(.//input)=3] + [count(.//input)=2] ' ); } diff --git a/src/Symfony/Component/Form/Tests/AbstractLayoutTest.php b/src/Symfony/Component/Form/Tests/AbstractLayoutTest.php index 5d48b9921cffb..32aabb86a6707 100644 --- a/src/Symfony/Component/Form/Tests/AbstractLayoutTest.php +++ b/src/Symfony/Component/Form/Tests/AbstractLayoutTest.php @@ -646,13 +646,12 @@ public function testSingleChoiceExpanded() $this->assertWidgetMatchesXpath($form->createView(), array(), '/div [ - ./input[@type="hidden"][@id="name__token"] - /following-sibling::input[@type="radio"][@name="name"][@id="name_0"][@value="&a"][@checked] + ./input[@type="radio"][@name="name"][@id="name_0"][@value="&a"][@checked] /following-sibling::label[@for="name_0"][.="[trans]Choice&A[/trans]"] /following-sibling::input[@type="radio"][@name="name"][@id="name_1"][@value="&b"][not(@checked)] /following-sibling::label[@for="name_1"][.="[trans]Choice&B[/trans]"] ] - [count(./input)=3] + [count(./input)=2] ' ); } @@ -669,13 +668,12 @@ public function testSingleChoiceExpandedSkipEmptyValue() $this->assertWidgetMatchesXpath($form->createView(), array(), '/div [ - ./input[@type="hidden"][@id="name__token"] - /following-sibling::input[@type="radio"][@name="name"][@id="name_0"][@checked] + ./input[@type="radio"][@name="name"][@id="name_0"][@checked] /following-sibling::label[@for="name_0"][.="[trans]Choice&A[/trans]"] /following-sibling::input[@type="radio"][@name="name"][@id="name_1"][not(@checked)] /following-sibling::label[@for="name_1"][.="[trans]Choice&B[/trans]"] ] - [count(./input)=3] + [count(./input)=2] ' ); } @@ -691,13 +689,12 @@ public function testSingleChoiceExpandedWithBooleanValue() $this->assertWidgetMatchesXpath($form->createView(), array(), '/div [ - ./input[@type="hidden"][@id="name__token"] - /following-sibling::input[@type="radio"][@name="name"][@id="name_0"][@checked] + ./input[@type="radio"][@name="name"][@id="name_0"][@checked] /following-sibling::label[@for="name_0"][.="[trans]Choice&A[/trans]"] /following-sibling::input[@type="radio"][@name="name"][@id="name_1"][not(@checked)] /following-sibling::label[@for="name_1"][.="[trans]Choice&B[/trans]"] ] - [count(./input)=3] + [count(./input)=2] ' ); } @@ -714,15 +711,14 @@ public function testMultipleChoiceExpanded() $this->assertWidgetMatchesXpath($form->createView(), array(), '/div [ - ./input[@type="hidden"][@id="name__token"] - /following-sibling::input[@type="checkbox"][@name="name[]"][@id="name_0"][@checked][not(@required)] + ./input[@type="checkbox"][@name="name[]"][@id="name_0"][@checked][not(@required)] /following-sibling::label[@for="name_0"][.="[trans]Choice&A[/trans]"] /following-sibling::input[@type="checkbox"][@name="name[]"][@id="name_1"][not(@checked)][not(@required)] /following-sibling::label[@for="name_1"][.="[trans]Choice&B[/trans]"] /following-sibling::input[@type="checkbox"][@name="name[]"][@id="name_2"][@checked][not(@required)] /following-sibling::label[@for="name_2"][.="[trans]Choice&C[/trans]"] ] - [count(./input)=4] + [count(./input)=3] ' ); } @@ -757,6 +753,22 @@ public function testCountryWithEmptyValue() ); } + public function testCsrf() + { + $this->csrfProvider->expects($this->any()) + ->method('generateCsrfToken') + ->will($this->returnValue('foo&bar')); + + $form = $this->factory->createNamed('csrf', 'name'); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/input + [@type="hidden"] + [@value="foo&bar"] +' + ); + } + public function testDateTime() { $form = $this->factory->createNamed('datetime', 'name', '2011-02-03 04:05:06', array( diff --git a/src/Symfony/Component/Form/Tests/AbstractTableLayoutTest.php b/src/Symfony/Component/Form/Tests/AbstractTableLayoutTest.php index 0943b06c4162d..d47b15564f0f3 100644 --- a/src/Symfony/Component/Form/Tests/AbstractTableLayoutTest.php +++ b/src/Symfony/Component/Form/Tests/AbstractTableLayoutTest.php @@ -45,12 +45,7 @@ public function testRepeatedRow() $html = $this->renderRow($form->createView()); $this->assertMatchesXpath($html, -'/tr[@style="display: none"] - [./td[@colspan="2"]/input - [@type="hidden"] - [@id="name__token"] - ] -/following-sibling::tr +'/tr [ ./td [./label[@for="name_first"]] @@ -64,7 +59,7 @@ public function testRepeatedRow() /following-sibling::td [./input[@id="name_second"]] ] - [count(../tr)=3] + [count(../tr)=2] ' ); } @@ -81,11 +76,6 @@ public function testRepeatedRowWithErrors() [./td[@colspan="2"]/ul [./li[.="[trans]Error![/trans]"]] ] -/following-sibling::tr[@style="display: none"] - [./td[@colspan="2"]/input - [@type="hidden"] - [@id="name__token"] - ] /following-sibling::tr [ ./td @@ -100,7 +90,7 @@ public function testRepeatedRowWithErrors() /following-sibling::td [./input[@id="name_second"]] ] - [count(../tr)=4] + [count(../tr)=3] ' ); } @@ -161,9 +151,9 @@ public function testCollection() $this->assertWidgetMatchesXpath($form->createView(), array(), '/table [ - ./tr[@style="display: none"][./td[@colspan="2"]/input[@type="hidden"][@id="name__token"]] - /following-sibling::tr[./td/input[@type="text"][@value="a"]] + ./tr[./td/input[@type="text"][@value="a"]] /following-sibling::tr[./td/input[@type="text"][@value="b"]] + /following-sibling::tr[./td/input[@type="hidden"][@id="name__token"]] ] [count(./tr[./td/input])=3] ' @@ -210,10 +200,7 @@ public function testForm() public function testNestedFormError() { $form = $this->factory->createNamedBuilder('form', 'name') - ->add($this->factory - ->createNamedBuilder('form', 'child', null, array('error_bubbling' => false)) - ->add('grandChild', 'form') - ) + ->add('child', 'form', array('error_bubbling' => false)) ->getForm(); $form->get('child')->addError(new FormError('Error!')); @@ -230,34 +217,6 @@ public function testNestedFormError() ); } - public function testCsrf() - { - $this->csrfProvider->expects($this->any()) - ->method('generateCsrfToken') - ->will($this->returnValue('foo&bar')); - - $form = $this->factory->createNamedBuilder('form', 'name') - ->add($this->factory - // No CSRF protection on nested forms - ->createNamedBuilder('form', 'child') - ->add($this->factory->createNamedBuilder('text', 'grandchild')) - ) - ->getForm(); - - $this->assertWidgetMatchesXpath($form->createView(), array(), -'/table - [ - ./tr[@style="display: none"] - [./td[@colspan="2"]/input - [@type="hidden"] - [@id="name__token"] - ] - ] - [count(.//input[@type="hidden"])=1] -' - ); - } - public function testRepeated() { $form = $this->factory->createNamed('repeated', 'name', 'foobar', array( @@ -267,12 +226,7 @@ public function testRepeated() $this->assertWidgetMatchesXpath($form->createView(), array(), '/table [ - ./tr[@style="display: none"] - [./td[@colspan="2"]/input - [@type="hidden"] - [@id="name__token"] - ] - /following-sibling::tr + ./tr [ ./td [./label[@for="name_first"]] @@ -287,7 +241,7 @@ public function testRepeated() [./input[@type="text"][@id="name_second"]] ] ] - [count(.//input)=3] + [count(.//input)=2] ' ); } @@ -303,12 +257,7 @@ public function testRepeatedWithCustomOptions() $this->assertWidgetMatchesXpath($form->createView(), array(), '/table [ - ./tr[@style="display: none"] - [./td[@colspan="2"]/input - [@type="hidden"] - [@id="name__token"] - ] - /following-sibling::tr + ./tr [ ./td [./label[@for="name_first"][.="[trans]Test[/trans]"]] @@ -323,7 +272,7 @@ public function testRepeatedWithCustomOptions() [./input[@type="password"][@id="name_second"][@required="required"]] ] ] - [count(.//input)=3] + [count(.//input)=2] ' ); } diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/Type/ChoiceTypeTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/Type/ChoiceTypeTest.php index fbda65a3b6bee..9cf54e25ede73 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Core/Type/ChoiceTypeTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Core/Type/ChoiceTypeTest.php @@ -98,7 +98,7 @@ public function testEitherChoiceListOrChoicesMustBeSet() )); } - public function testExpandedChoicesOptionsTurnIntoChildren() + public function testExpandedChoicesOptionsTurnIntoFields() { $form = $this->factory->create('choice', null, array( 'expanded' => true, @@ -141,7 +141,7 @@ public function testExpandedCheckboxesAreNeverRequired() } } - public function testExpandedRadiosAreRequiredIfChoiceChildIsRequired() + public function testExpandedRadiosAreRequiredIfChoiceFieldIsRequired() { $form = $this->factory->create('choice', null, array( 'multiple' => false, @@ -155,7 +155,7 @@ public function testExpandedRadiosAreRequiredIfChoiceChildIsRequired() } } - public function testExpandedRadiosAreNotRequiredIfChoiceChildIsNotRequired() + public function testExpandedRadiosAreNotRequiredIfChoiceFieldIsNotRequired() { $form = $this->factory->create('choice', null, array( 'multiple' => false, @@ -288,7 +288,7 @@ public function testBindSingleExpandedNothingChecked() $this->assertNull($form[4]->getClientData()); } - public function testBindSingleExpandedWithFalseDoesNotHaveExtraChildren() + public function testBindSingleExpandedWithFalseDoesNotHaveExtraFields() { $form = $this->factory->create('choice', null, array( 'multiple' => false, @@ -302,7 +302,7 @@ public function testBindSingleExpandedWithFalseDoesNotHaveExtraChildren() $this->assertNull($form->getData()); } - public function testBindSingleExpandedWithEmptyChild() + public function testBindSingleExpandedWithEmptyField() { $form = $this->factory->create('choice', null, array( 'multiple' => false, @@ -422,7 +422,7 @@ public function testBindMultipleExpandedEmpty() $this->assertNull($form[4]->getClientData()); } - public function testBindMultipleExpandedWithEmptyChild() + public function testBindMultipleExpandedWithEmptyField() { $form = $this->factory->create('choice', null, array( 'multiple' => true, diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/Type/CollectionTypeTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/Type/CollectionTypeTest.php index 5461258e95571..b0bb62508f6fa 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Core/Type/CollectionTypeTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Core/Type/CollectionTypeTest.php @@ -15,10 +15,10 @@ class CollectionTypeTest extends TypeTestCase { - public function testContainsNoChildByDefault() + public function testContainsNoFieldByDefault() { $form = $this->factory->create('collection', null, array( - 'type' => 'form', + 'type' => 'field', )); $this->assertCount(0, $form); @@ -27,7 +27,7 @@ public function testContainsNoChildByDefault() public function testSetDataAdjustsSize() { $form = $this->factory->create('collection', null, array( - 'type' => 'form', + 'type' => 'field', 'options' => array( 'max_length' => 20, ), @@ -53,7 +53,7 @@ public function testSetDataAdjustsSize() public function testThrowsExceptionIfObjectIsNotTraversable() { $form = $this->factory->create('collection', null, array( - 'type' => 'form', + 'type' => 'field', )); $this->setExpectedException('Symfony\Component\Form\Exception\UnexpectedTypeException'); $form->setData(new \stdClass()); @@ -62,7 +62,7 @@ public function testThrowsExceptionIfObjectIsNotTraversable() public function testNotResizedIfBoundWithMissingData() { $form = $this->factory->create('collection', null, array( - 'type' => 'form', + 'type' => 'field', )); $form->setData(array('foo@foo.com', 'bar@bar.com')); $form->bind(array('foo@bar.com')); @@ -76,7 +76,7 @@ public function testNotResizedIfBoundWithMissingData() public function testResizedDownIfBoundWithMissingDataAndAllowDelete() { $form = $this->factory->create('collection', null, array( - 'type' => 'form', + 'type' => 'field', 'allow_delete' => true, )); $form->setData(array('foo@foo.com', 'bar@bar.com')); @@ -91,7 +91,7 @@ public function testResizedDownIfBoundWithMissingDataAndAllowDelete() public function testNotResizedIfBoundWithExtraData() { $form = $this->factory->create('collection', null, array( - 'type' => 'form', + 'type' => 'field', )); $form->setData(array('foo@bar.com')); $form->bind(array('foo@foo.com', 'bar@bar.com')); @@ -104,7 +104,7 @@ public function testNotResizedIfBoundWithExtraData() public function testResizedUpIfBoundWithExtraDataAndAllowAdd() { $form = $this->factory->create('collection', null, array( - 'type' => 'form', + 'type' => 'field', 'allow_add' => true, )); $form->setData(array('foo@bar.com')); @@ -120,7 +120,7 @@ public function testResizedUpIfBoundWithExtraDataAndAllowAdd() public function testAllowAddButNoPrototype() { $form = $this->factory->create('collection', null, array( - 'type' => 'form', + 'type' => 'field', 'allow_add' => true, 'prototype' => false, )); @@ -169,7 +169,7 @@ public function testGetDataDoesNotContainsPrototypeNameAfterDataAreSet() public function testPrototypeNameOption() { $form = $this->factory->create('collection', null, array( - 'type' => 'form', + 'type' => 'field', 'prototype' => true, 'allow_add' => true, )); @@ -177,7 +177,7 @@ public function testPrototypeNameOption() $this->assertSame('__name__', $form->getAttribute('prototype')->getName(), '__name__ is the default'); $form = $this->factory->create('collection', null, array( - 'type' => 'form', + 'type' => 'field', 'prototype' => true, 'allow_add' => true, 'prototype_name' => '__test__', diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/Type/FieldTypeTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/Type/FieldTypeTest.php new file mode 100644 index 0000000000000..b588d4433f250 --- /dev/null +++ b/src/Symfony/Component/Form/Tests/Extension/Core/Type/FieldTypeTest.php @@ -0,0 +1,380 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Extension\Core\Type; + +use Symfony\Component\Form\Util\PropertyPath; +use Symfony\Component\Form\Form; +use Symfony\Component\Form\Tests\Fixtures\Author; +use Symfony\Component\Form\Tests\Fixtures\FixedDataTransformer; + +class FieldTypeTest extends TypeTestCase +{ + public function testGetPropertyPathDefaultPath() + { + $form = $this->factory->createNamed('field', 'title'); + + $this->assertEquals(new PropertyPath('title'), $form->getAttribute('property_path')); + } + + public function testGetPropertyPathPathIsZero() + { + $form = $this->factory->create('field', null, array('property_path' => '0')); + + $this->assertEquals(new PropertyPath('0'), $form->getAttribute('property_path')); + } + + public function testGetPropertyPathPathIsEmpty() + { + $form = $this->factory->create('field', null, array('property_path' => '')); + + $this->assertNull($form->getAttribute('property_path')); + } + + public function testGetPropertyPathPathIsFalse() + { + $form = $this->factory->create('field', null, array('property_path' => false)); + + $this->assertNull($form->getAttribute('property_path')); + } + + public function testGetPropertyPathPathIsNull() + { + $form = $this->factory->createNamed('field', 'title', null, array('property_path' => null)); + + $this->assertEquals(new PropertyPath('title'), $form->getAttribute('property_path')); + } + + public function testPassRequiredAsOption() + { + $form = $this->factory->create('field', null, array('required' => false)); + + $this->assertFalse($form->isRequired()); + + $form = $this->factory->create('field', null, array('required' => true)); + + $this->assertTrue($form->isRequired()); + } + + public function testPassDisabledAsOption() + { + $form = $this->factory->create('field', null, array('disabled' => true)); + + $this->assertTrue($form->isDisabled()); + } + + public function testBoundDataIsTrimmedBeforeTransforming() + { + $form = $this->factory->createBuilder('field') + ->appendClientTransformer(new FixedDataTransformer(array( + null => '', + 'reverse[a]' => 'a', + ))) + ->getForm(); + + $form->bind(' a '); + + $this->assertEquals('a', $form->getClientData()); + $this->assertEquals('reverse[a]', $form->getData()); + } + + public function testBoundDataIsNotTrimmedBeforeTransformingIfNoTrimming() + { + $form = $this->factory->createBuilder('field', null, array('trim' => false)) + ->appendClientTransformer(new FixedDataTransformer(array( + null => '', + 'reverse[ a ]' => ' a ', + ))) + ->getForm(); + + $form->bind(' a '); + + $this->assertEquals(' a ', $form->getClientData()); + $this->assertEquals('reverse[ a ]', $form->getData()); + } + + public function testPassIdAndNameToView() + { + $form = $this->factory->createNamed('field', 'name'); + $view = $form->createView(); + + $this->assertEquals('name', $view->get('id')); + $this->assertEquals('name', $view->get('name')); + $this->assertEquals('name', $view->get('full_name')); + } + + public function testStripLeadingUnderscoresAndDigitsFromId() + { + $form = $this->factory->createNamed('field', '_09name'); + $view = $form->createView(); + + $this->assertEquals('name', $view->get('id')); + $this->assertEquals('_09name', $view->get('name')); + $this->assertEquals('_09name', $view->get('full_name')); + } + + public function testPassIdAndNameToViewWithParent() + { + $parent = $this->factory->createNamed('field', 'parent'); + $parent->add($this->factory->createNamed('field', 'child')); + $view = $parent->createView(); + + $this->assertEquals('parent_child', $view['child']->get('id')); + $this->assertEquals('child', $view['child']->get('name')); + $this->assertEquals('parent[child]', $view['child']->get('full_name')); + } + + public function testPassIdAndNameToViewWithGrandParent() + { + $parent = $this->factory->createNamed('field', 'parent'); + $parent->add($this->factory->createNamed('field', 'child')); + $parent['child']->add($this->factory->createNamed('field', 'grand_child')); + $view = $parent->createView(); + + $this->assertEquals('parent_child_grand_child', $view['child']['grand_child']->get('id')); + $this->assertEquals('grand_child', $view['child']['grand_child']->get('name')); + $this->assertEquals('parent[child][grand_child]', $view['child']['grand_child']->get('full_name')); + } + + public function testNonReadOnlyFieldWithReadOnlyParentBeingReadOnly() + { + $parent = $this->factory->createNamed('field', 'parent', null, array('read_only' => true)); + $child = $this->factory->createNamed('field', 'child'); + $view = $parent->add($child)->createView(); + + $this->assertTrue($view['child']->get('read_only')); + } + + public function testReadOnlyFieldWithNonReadOnlyParentBeingReadOnly() + { + $parent = $this->factory->createNamed('field', 'parent'); + $child = $this->factory->createNamed('field', 'child', null, array('read_only' => true)); + $view = $parent->add($child)->createView(); + + $this->assertTrue($view['child']->get('read_only')); + } + + public function testNonReadOnlyFieldWithNonReadOnlyParentBeingNonReadOnly() + { + $parent = $this->factory->createNamed('field', 'parent'); + $child = $this->factory->createNamed('field', 'child'); + $view = $parent->add($child)->createView(); + + $this->assertFalse($view['child']->get('read_only')); + } + + public function testPassMaxLengthToView() + { + $form = $this->factory->create('field', null, array('max_length' => 10)); + $view = $form->createView(); + + $this->assertSame(10, $view->get('max_length')); + } + + public function testPassTranslationDomainToView() + { + $form = $this->factory->create('field', null, array('translation_domain' => 'test')); + $view = $form->createView(); + + $this->assertSame('test', $view->get('translation_domain')); + } + + public function testPassDefaultLabelToView() + { + $form = $this->factory->createNamed('field', '__test___field'); + $view = $form->createView(); + + $this->assertSame('Test field', $view->get('label')); + } + + public function testPassLabelToView() + { + $form = $this->factory->createNamed('field', '__test___field', null, array('label' => 'My label')); + $view = $form->createView(); + + $this->assertSame('My label', $view->get('label')); + } + + public function testDefaultTranslationDomain() + { + $form = $this->factory->create('field'); + $view = $form->createView(); + + $this->assertSame('messages', $view->get('translation_domain')); + } + + public function testBindWithEmptyDataCreatesObjectIfClassAvailable() + { + $form = $this->factory->create('form', null, array( + 'data_class' => 'Symfony\Component\Form\Tests\Fixtures\Author', + 'required' => false, + )); + $form->add($this->factory->createNamed('field', 'firstName')); + $form->add($this->factory->createNamed('field', 'lastName')); + + $form->setData(null); + // partially empty, still an object is created + $form->bind(array('firstName' => 'Bernhard', 'lastName' => '')); + + $author = new Author(); + $author->firstName = 'Bernhard'; + $author->setLastName(''); + + $this->assertEquals($author, $form->getData()); + } + + public function testBindWithEmptyDataCreatesObjectIfInitiallyBoundWithObject() + { + $form = $this->factory->create('form', null, array( + // data class is inferred from the passed object + 'data' => new Author(), + 'required' => false, + )); + $form->add($this->factory->createNamed('field', 'firstName')); + $form->add($this->factory->createNamed('field', 'lastName')); + + $form->setData(null); + // partially empty, still an object is created + $form->bind(array('firstName' => 'Bernhard', 'lastName' => '')); + + $author = new Author(); + $author->firstName = 'Bernhard'; + $author->setLastName(''); + + $this->assertEquals($author, $form->getData()); + } + + public function testBindWithEmptyDataDoesNotCreateObjectIfDataClassIsNull() + { + $form = $this->factory->create('form', null, array( + 'data' => new Author(), + 'data_class' => null, + 'required' => false, + )); + $form->add($this->factory->createNamed('field', 'firstName')); + + $form->setData(null); + $form->bind(array('firstName' => 'Bernhard')); + + $this->assertSame(array('firstName' => 'Bernhard'), $form->getData()); + } + + public function testBindEmptyWithEmptyDataCreatesNoObjectIfNotRequired() + { + $form = $this->factory->create('form', null, array( + 'data_class' => 'Symfony\Component\Form\Tests\Fixtures\Author', + 'required' => false, + )); + $form->add($this->factory->createNamed('field', 'firstName')); + $form->add($this->factory->createNamed('field', 'lastName')); + + $form->setData(null); + $form->bind(array('firstName' => '', 'lastName' => '')); + + $this->assertNull($form->getData()); + } + + public function testBindEmptyWithEmptyDataCreatesObjectIfRequired() + { + $form = $this->factory->create('form', null, array( + 'data_class' => 'Symfony\Component\Form\Tests\Fixtures\Author', + 'required' => true, + )); + $form->add($this->factory->createNamed('field', 'firstName')); + $form->add($this->factory->createNamed('field', 'lastName')); + + $form->setData(null); + $form->bind(array('firstName' => '', 'lastName' => '')); + + $this->assertEquals(new Author(), $form->getData()); + } + + /* + * We need something to write the field values into + */ + public function testBindWithEmptyDataStoresArrayIfNoClassAvailable() + { + $form = $this->factory->create('form'); + $form->add($this->factory->createNamed('field', 'firstName')); + + $form->setData(null); + $form->bind(array('firstName' => 'Bernhard')); + + $this->assertSame(array('firstName' => 'Bernhard'), $form->getData()); + } + + public function testBindWithEmptyDataUsesEmptyDataOption() + { + $author = new Author(); + + $form = $this->factory->create('form', null, array( + 'empty_data' => $author, + )); + $form->add($this->factory->createNamed('field', 'firstName')); + + $form->bind(array('firstName' => 'Bernhard')); + + $this->assertSame($author, $form->getData()); + $this->assertEquals('Bernhard', $author->firstName); + } + + public function testGetAttributesIsEmpty() + { + $form = $this->factory->create('field', null, array('attr' => array())); + + $this->assertCount(0, $form->getAttribute('attr')); + } + + /** + * @see https://github.com/symfony/symfony/issues/1986 + */ + public function testSetDataThroughParamsWithZero() + { + $form = $this->factory->create('field', null, array('data' => 0)); + $view = $form->createView(); + + $this->assertFalse($form->isEmpty()); + + $this->assertSame('0', $view->get('value')); + $this->assertSame('0', $form->getData()); + + $form = $this->factory->create('field', null, array('data' => '0')); + $view = $form->createView(); + + $this->assertFalse($form->isEmpty()); + + $this->assertSame('0', $view->get('value')); + $this->assertSame('0', $form->getData()); + + $form = $this->factory->create('field', null, array('data' => '00000')); + $view = $form->createView(); + + $this->assertFalse($form->isEmpty()); + + $this->assertSame('00000', $view->get('value')); + $this->assertSame('00000', $form->getData()); + } + + /** + * @expectedException Symfony\Component\Form\Exception\FormException + */ + public function testAttributesException() + { + $form = $this->factory->create('field', null, array('attr' => '')); + } + + public function testNameCanBeEmptyString() + { + $form = $this->factory->createNamed('field', ''); + + $this->assertEquals('', $form->getName()); + } +} 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 8275c61455999..eff0719692781 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Core/Type/FormTypeTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Core/Type/FormTypeTest.php @@ -11,11 +11,9 @@ namespace Symfony\Component\Form\Tests\Extension\Core\Type; -use Symfony\Component\Form\Util\PropertyPath; use Symfony\Component\Form\Form; use Symfony\Component\Form\CallbackTransformer; use Symfony\Component\Form\Tests\Fixtures\Author; -use Symfony\Component\Form\Tests\Fixtures\FixedDataTransformer; class FormTest_AuthorWithoutRefSetter { @@ -51,388 +49,13 @@ public function setReferenceCopy($reference) class FormTypeTest extends TypeTestCase { - public function testGetPropertyPathDefaultPath() - { - $form = $this->factory->createNamed('form', 'title'); - - $this->assertEquals(new PropertyPath('title'), $form->getAttribute('property_path')); - } - - public function testGetPropertyPathPathIsZero() - { - $form = $this->factory->create('form', null, array('property_path' => '0')); - - $this->assertEquals(new PropertyPath('0'), $form->getAttribute('property_path')); - } - - public function testGetPropertyPathPathIsEmpty() - { - $form = $this->factory->create('form', null, array('property_path' => '')); - - $this->assertNull($form->getAttribute('property_path')); - } - - public function testGetPropertyPathPathIsFalse() - { - $form = $this->factory->create('form', null, array('property_path' => false)); - - $this->assertNull($form->getAttribute('property_path')); - } - - public function testGetPropertyPathPathIsNull() - { - $form = $this->factory->createNamed('form', 'title', null, array('property_path' => null)); - - $this->assertEquals(new PropertyPath('title'), $form->getAttribute('property_path')); - } - - public function testPassRequiredAsOption() - { - $form = $this->factory->create('form', null, array('required' => false)); - - $this->assertFalse($form->isRequired()); - - $form = $this->factory->create('form', null, array('required' => true)); - - $this->assertTrue($form->isRequired()); - } - - public function testPassDisabledAsOption() - { - $form = $this->factory->create('form', null, array('disabled' => true)); - - $this->assertTrue($form->isDisabled()); - } - - public function testBoundDataIsTrimmedBeforeTransforming() - { - $form = $this->factory->createBuilder('form') - ->appendClientTransformer(new FixedDataTransformer(array( - null => '', - 'reverse[a]' => 'a', - ))) - ->getForm(); - - $form->bind(' a '); - - $this->assertEquals('a', $form->getClientData()); - $this->assertEquals('reverse[a]', $form->getData()); - } - - public function testBoundDataIsNotTrimmedBeforeTransformingIfNoTrimming() - { - $form = $this->factory->createBuilder('form', null, array('trim' => false)) - ->appendClientTransformer(new FixedDataTransformer(array( - null => '', - 'reverse[ a ]' => ' a ', - ))) - ->getForm(); - - $form->bind(' a '); - - $this->assertEquals(' a ', $form->getClientData()); - $this->assertEquals('reverse[ a ]', $form->getData()); - } - - public function testPassIdAndNameToView() - { - $form = $this->factory->createNamed('form', 'name'); - $view = $form->createView(); - - $this->assertEquals('name', $view->get('id')); - $this->assertEquals('name', $view->get('name')); - $this->assertEquals('name', $view->get('full_name')); - } - - public function testStripLeadingUnderscoresAndDigitsFromId() - { - $form = $this->factory->createNamed('form', '_09name'); - $view = $form->createView(); - - $this->assertEquals('name', $view->get('id')); - $this->assertEquals('_09name', $view->get('name')); - $this->assertEquals('_09name', $view->get('full_name')); - } - - public function testPassIdAndNameToViewWithParent() - { - $parent = $this->factory->createNamed('form', 'parent'); - $parent->add($this->factory->createNamed('form', 'child')); - $view = $parent->createView(); - - $this->assertEquals('parent_child', $view['child']->get('id')); - $this->assertEquals('child', $view['child']->get('name')); - $this->assertEquals('parent[child]', $view['child']->get('full_name')); - } - - public function testPassIdAndNameToViewWithGrandParent() - { - $parent = $this->factory->createNamed('form', 'parent'); - $parent->add($this->factory->createNamed('form', 'child')); - $parent['child']->add($this->factory->createNamed('form', 'grand_child')); - $view = $parent->createView(); - - $this->assertEquals('parent_child_grand_child', $view['child']['grand_child']->get('id')); - $this->assertEquals('grand_child', $view['child']['grand_child']->get('name')); - $this->assertEquals('parent[child][grand_child]', $view['child']['grand_child']->get('full_name')); - } - - public function testNonReadOnlyFormWithReadOnlyParentBeingReadOnly() - { - $parent = $this->factory->createNamed('form', 'parent', null, array('read_only' => true)); - $child = $this->factory->createNamed('form', 'child'); - $view = $parent->add($child)->createView(); - - $this->assertTrue($view['child']->get('read_only')); - } - - public function testReadOnlyFormWithNonReadOnlyParentBeingReadOnly() - { - $parent = $this->factory->createNamed('form', 'parent'); - $child = $this->factory->createNamed('form', 'child', null, array('read_only' => true)); - $view = $parent->add($child)->createView(); - - $this->assertTrue($view['child']->get('read_only')); - } - - public function testNonReadOnlyFormWithNonReadOnlyParentBeingNonReadOnly() - { - $parent = $this->factory->createNamed('form', 'parent'); - $child = $this->factory->createNamed('form', 'child'); - $view = $parent->add($child)->createView(); - - $this->assertFalse($view['child']->get('read_only')); - } - - public function testPassMaxLengthToView() - { - $form = $this->factory->create('form', null, array('max_length' => 10)); - $view = $form->createView(); - - $this->assertSame(10, $view->get('max_length')); - } - - public function testPassTranslationDomainToView() - { - $form = $this->factory->create('form', null, array('translation_domain' => 'test')); - $view = $form->createView(); - - $this->assertSame('test', $view->get('translation_domain')); - } - - public function testPassDefaultLabelToView() - { - $form = $this->factory->createNamed('form', '__test___field'); - $view = $form->createView(); - - $this->assertSame('Test field', $view->get('label')); - } - - public function testPassLabelToView() - { - $form = $this->factory->createNamed('form', '__test___field', null, array('label' => 'My label')); - $view = $form->createView(); - - $this->assertSame('My label', $view->get('label')); - } - - public function testDefaultTranslationDomain() - { - $form = $this->factory->create('form'); - $view = $form->createView(); - - $this->assertSame('messages', $view->get('translation_domain')); - } - - public function testBindWithEmptyDataCreatesObjectIfClassAvailable() - { - $form = $this->factory->create('form', null, array( - 'data_class' => 'Symfony\Component\Form\Tests\Fixtures\Author', - 'required' => false, - )); - $form->add($this->factory->createNamed('form', 'firstName')); - $form->add($this->factory->createNamed('form', 'lastName')); - - $form->setData(null); - // partially empty, still an object is created - $form->bind(array('firstName' => 'Bernhard', 'lastName' => '')); - - $author = new Author(); - $author->firstName = 'Bernhard'; - $author->setLastName(''); - - $this->assertEquals($author, $form->getData()); - } - - public function testBindWithEmptyDataCreatesObjectIfInitiallyBoundWithObject() - { - $form = $this->factory->create('form', null, array( - // data class is inferred from the passed object - 'data' => new Author(), - 'required' => false, - )); - $form->add($this->factory->createNamed('form', 'firstName')); - $form->add($this->factory->createNamed('form', 'lastName')); - - $form->setData(null); - // partially empty, still an object is created - $form->bind(array('firstName' => 'Bernhard', 'lastName' => '')); - - $author = new Author(); - $author->firstName = 'Bernhard'; - $author->setLastName(''); - - $this->assertEquals($author, $form->getData()); - } - - public function testBindWithEmptyDataDoesNotCreateObjectIfDataClassIsNull() - { - $form = $this->factory->create('form', null, array( - 'data' => new Author(), - 'data_class' => null, - 'required' => false, - )); - $form->add($this->factory->createNamed('form', 'firstName')); - - $form->setData(null); - $form->bind(array('firstName' => 'Bernhard')); - - $this->assertSame(array('firstName' => 'Bernhard'), $form->getData()); - } - - public function testBindEmptyWithEmptyDataCreatesNoObjectIfNotRequired() - { - $form = $this->factory->create('form', null, array( - 'data_class' => 'Symfony\Component\Form\Tests\Fixtures\Author', - 'required' => false, - )); - $form->add($this->factory->createNamed('form', 'firstName')); - $form->add($this->factory->createNamed('form', 'lastName')); - - $form->setData(null); - $form->bind(array('firstName' => '', 'lastName' => '')); - - $this->assertNull($form->getData()); - } - - public function testBindEmptyWithEmptyDataCreatesObjectIfRequired() - { - $form = $this->factory->create('form', null, array( - 'data_class' => 'Symfony\Component\Form\Tests\Fixtures\Author', - 'required' => true, - )); - $form->add($this->factory->createNamed('form', 'firstName')); - $form->add($this->factory->createNamed('form', 'lastName')); - - $form->setData(null); - $form->bind(array('firstName' => '', 'lastName' => '')); - - $this->assertEquals(new Author(), $form->getData()); - } - - /* - * We need something to write the field values into - */ - public function testBindWithEmptyDataStoresArrayIfNoClassAvailable() - { - $form = $this->factory->create('form'); - $form->add($this->factory->createNamed('form', 'firstName')); - - $form->setData(null); - $form->bind(array('firstName' => 'Bernhard')); - - $this->assertSame(array('firstName' => 'Bernhard'), $form->getData()); - } - - public function testBindWithEmptyDataPassesEmptyStringToTransformerIfNoChildren() - { - $form = $this->factory->createBuilder('form') - ->appendClientTransformer(new FixedDataTransformer(array( - // required for the initial, internal setData(null) - null => 'null', - // required to test that bind(null) is converted to '' - 'empty' => '', - ))) - ->getForm(); - - $form->bind(null); - - $this->assertSame('empty', $form->getData()); - } - - public function testBindWithEmptyDataUsesEmptyDataOption() - { - $author = new Author(); - - $form = $this->factory->create('form', null, array( - 'empty_data' => $author, - )); - $form->add($this->factory->createNamed('form', 'firstName')); - - $form->bind(array('firstName' => 'Bernhard')); - - $this->assertSame($author, $form->getData()); - $this->assertEquals('Bernhard', $author->firstName); - } - - public function testGetAttributesIsEmpty() - { - $form = $this->factory->create('form', null, array('attr' => array())); - - $this->assertCount(0, $form->getAttribute('attr')); - } - - /** - * @see https://github.com/symfony/symfony/issues/1986 - */ - public function testSetDataThroughParamsWithZero() - { - $form = $this->factory->create('form', null, array('data' => 0)); - $view = $form->createView(); - - $this->assertFalse($form->isEmpty()); - - $this->assertSame('0', $view->get('value')); - $this->assertSame('0', $form->getData()); - - $form = $this->factory->create('form', null, array('data' => '0')); - $view = $form->createView(); - - $this->assertFalse($form->isEmpty()); - - $this->assertSame('0', $view->get('value')); - $this->assertSame('0', $form->getData()); - - $form = $this->factory->create('form', null, array('data' => '00000')); - $view = $form->createView(); - - $this->assertFalse($form->isEmpty()); - - $this->assertSame('00000', $view->get('value')); - $this->assertSame('00000', $form->getData()); - } - - /** - * @expectedException Symfony\Component\Form\Exception\FormException - */ - public function testAttributesException() - { - $form = $this->factory->create('form', null, array('attr' => '')); - } - - public function testNameCanBeEmptyString() - { - $form = $this->factory->createNamed('form', ''); - - $this->assertEquals('', $form->getName()); - } public function testSubformDoesntCallSetters() { $author = new FormTest_AuthorWithoutRefSetter(new Author()); $builder = $this->factory->createBuilder('form'); $builder->add('reference', 'form'); - $builder->get('reference')->add('firstName', 'form'); + $builder->get('reference')->add('firstName', 'field'); $builder->setData($author); $form = $builder->getForm(); @@ -454,7 +77,7 @@ public function testSubformCallsSettersIfTheObjectChanged() $builder = $this->factory->createBuilder('form'); $builder->add('referenceCopy', 'form'); - $builder->get('referenceCopy')->add('firstName', 'form'); + $builder->get('referenceCopy')->add('firstName', 'field'); $builder->setData($author); $form = $builder->getForm(); @@ -476,7 +99,7 @@ public function testSubformCallsSettersIfByReferenceIsFalse() $builder = $this->factory->createBuilder('form'); $builder->add('referenceCopy', 'form', array('by_reference' => false)); - $builder->get('referenceCopy')->add('firstName', 'form'); + $builder->get('referenceCopy')->add('firstName', 'field'); $builder->setData($author); $form = $builder->getForm(); diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/Type/RepeatedTypeTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/Type/RepeatedTypeTest.php index 6d9139633ee94..df571ba9d82a5 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Core/Type/RepeatedTypeTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Core/Type/RepeatedTypeTest.php @@ -21,7 +21,7 @@ protected function setUp() parent::setUp(); $this->form = $this->factory->create('repeated', null, array( - 'type' => 'form', + 'type' => 'field', )); $this->form->setData(null); } @@ -37,7 +37,7 @@ public function testSetData() public function testSetOptions() { $form = $this->factory->create('repeated', null, array( - 'type' => 'form', + 'type' => 'field', 'options' => array('label' => 'Global'), )); @@ -47,11 +47,11 @@ public function testSetOptions() $this->assertTrue($form['second']->isRequired()); } - public function testSetOptionsPerChild() + public function testSetOptionsPerField() { $form = $this->factory->create('repeated', null, array( // the global required value cannot be overriden - 'type' => 'form', + 'type' => 'field', 'first_options' => array('label' => 'Test', 'required' => false), 'second_options' => array('label' => 'Test2') )); @@ -66,17 +66,17 @@ public function testSetRequired() { $form = $this->factory->create('repeated', null, array( 'required' => false, - 'type' => 'form', + 'type' => 'field', )); $this->assertFalse($form['first']->isRequired()); $this->assertFalse($form['second']->isRequired()); } - public function testSetOptionsPerChildAndOverwrite() + public function testSetOptionsPerFieldAndOverwrite() { $form = $this->factory->create('repeated', null, array( - 'type' => 'form', + 'type' => 'field', 'options' => array('label' => 'Label'), 'second_options' => array('label' => 'Second label') )); diff --git a/src/Symfony/Component/Form/Tests/Extension/Csrf/EventListener/EnsureCsrfFieldListenerTest.php b/src/Symfony/Component/Form/Tests/Extension/Csrf/EventListener/EnsureCsrfFieldListenerTest.php new file mode 100644 index 0000000000000..9b87717e5d036 --- /dev/null +++ b/src/Symfony/Component/Form/Tests/Extension/Csrf/EventListener/EnsureCsrfFieldListenerTest.php @@ -0,0 +1,87 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Extension\Csrf\EventListener; + +use Symfony\Component\Form\Event\DataEvent; +use Symfony\Component\Form\Extension\Csrf\EventListener\EnsureCsrfFieldListener; + +class EnsureCsrfFieldListenerTest extends \PHPUnit_Framework_TestCase +{ + private $form; + private $formFactory; + private $field; + private $event; + + protected function setUp() + { + if (!class_exists('Symfony\Component\EventDispatcher\EventDispatcher')) { + $this->markTestSkipped('The "EventDispatcher" component is not available'); + } + + $this->formFactory = $this->getMock('Symfony\\Component\\Form\\FormFactoryInterface'); + $this->form = $this->getMock('Symfony\\Component\\Form\\Tests\\FormInterface'); + $this->field = $this->getMock('Symfony\\Component\\Form\\Tests\\FormInterface'); + $this->event = new DataEvent($this->form, array()); + } + + protected function tearDown() + { + $this->form = null; + $this->formFactory = null; + $this->field = null; + $this->event = null; + } + + public function testAddField() + { + $this->formFactory->expects($this->once()) + ->method('createNamed') + ->with('csrf', '_token', null, array()) + ->will($this->returnValue($this->field)); + $this->form->expects($this->once()) + ->method('add') + ->with($this->isInstanceOf('Symfony\\Component\\Form\\Tests\\FormInterface')); + + $listener = new EnsureCsrfFieldListener($this->formFactory, '_token'); + $listener->ensureCsrfField($this->event); + } + + public function testIntention() + { + $this->formFactory->expects($this->once()) + ->method('createNamed') + ->with('csrf', '_token', null, array('intention' => 'something')) + ->will($this->returnValue($this->field)); + $this->form->expects($this->once()) + ->method('add') + ->with($this->isInstanceOf('Symfony\\Component\\Form\\Tests\\FormInterface')); + + $listener = new EnsureCsrfFieldListener($this->formFactory, '_token', 'something'); + $listener->ensureCsrfField($this->event); + } + + public function testProvider() + { + $provider = $this->getMock('Symfony\\Component\\Form\\Extension\\Csrf\\CsrfProvider\\CsrfProviderInterface'); + + $this->formFactory->expects($this->once()) + ->method('createNamed') + ->with('csrf', '_token', null, array('csrf_provider' => $provider)) + ->will($this->returnValue($this->field)); + $this->form->expects($this->once()) + ->method('add') + ->with($this->isInstanceOf('Symfony\\Component\\Form\\Tests\\FormInterface')); + + $listener = new EnsureCsrfFieldListener($this->formFactory, '_token', null, $provider); + $listener->ensureCsrfField($this->event); + } +} diff --git a/src/Symfony/Component/Form/Tests/Extension/Csrf/Type/CsrfTypeTest.php b/src/Symfony/Component/Form/Tests/Extension/Csrf/Type/CsrfTypeTest.php new file mode 100644 index 0000000000000..4483e7f3925b3 --- /dev/null +++ b/src/Symfony/Component/Form/Tests/Extension/Csrf/Type/CsrfTypeTest.php @@ -0,0 +1,112 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Extension\Csrf\Type; + +class CsrfTypeTest extends TypeTestCase +{ + protected $provider; + + protected function setUp() + { + parent::setUp(); + + $this->provider = $this->getMock('Symfony\Component\Form\Extension\Csrf\CsrfProvider\CsrfProviderInterface'); + } + + protected function tearDown() + { + parent::tearDown(); + + $this->provider = null; + } + + protected function getNonRootForm() + { + $form = $this->getMock('Symfony\Component\Form\Tests\FormInterface'); + $form->expects($this->any()) + ->method('isRoot') + ->will($this->returnValue(false)); + + return $form; + } + + public function testGenerateCsrfToken() + { + $this->provider->expects($this->once()) + ->method('generateCsrfToken') + ->with('%INTENTION%') + ->will($this->returnValue('token')); + + $form = $this->factory->create('csrf', null, array( + 'csrf_provider' => $this->provider, + 'intention' => '%INTENTION%' + )); + + $this->assertEquals('token', $form->getData()); + } + + public function testValidateTokenOnBind() + { + $this->provider->expects($this->once()) + ->method('isCsrfTokenValid') + ->with('%INTENTION%', 'token') + ->will($this->returnValue(true)); + + $form = $this->factory->create('csrf', null, array( + 'csrf_provider' => $this->provider, + 'intention' => '%INTENTION%' + )); + $form->bind('token'); + + $this->assertEquals('token', $form->getData()); + } + + public function testDontValidateTokenIfParentIsNotRoot() + { + $this->provider->expects($this->never()) + ->method('isCsrfTokenValid'); + + $form = $this->factory->create('csrf', null, array( + 'csrf_provider' => $this->provider, + 'intention' => '%INTENTION%' + )); + $form->setParent($this->getNonRootForm()); + $form->bind('token'); + } + + public function testCsrfTokenIsRegeneratedIfValidationFails() + { + $this->provider->expects($this->at(0)) + ->method('generateCsrfToken') + ->with('%INTENTION%') + ->will($this->returnValue('token1')); + $this->provider->expects($this->at(1)) + ->method('isCsrfTokenValid') + ->with('%INTENTION%', 'invalid') + ->will($this->returnValue(false)); + + // The token is regenerated to avoid stalled tokens, for example when + // the session ID changed + $this->provider->expects($this->at(2)) + ->method('generateCsrfToken') + ->with('%INTENTION%') + ->will($this->returnValue('token2')); + + $form = $this->factory->create('csrf', null, array( + 'csrf_provider' => $this->provider, + 'intention' => '%INTENTION%' + )); + $form->bind('invalid'); + + $this->assertEquals('token2', $form->getData()); + } +} diff --git a/src/Symfony/Component/Form/Tests/Extension/Csrf/Type/FormTypeCsrfExtensionTest.php b/src/Symfony/Component/Form/Tests/Extension/Csrf/Type/FormTypeCsrfExtensionTest.php index 9024002b5223b..20da376d7996c 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Csrf/Type/FormTypeCsrfExtensionTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Csrf/Type/FormTypeCsrfExtensionTest.php @@ -11,188 +11,43 @@ namespace Symfony\Component\Form\Tests\Extension\Csrf\Type; -use Symfony\Component\Form\Extension\Csrf\CsrfExtension; -use Symfony\Component\Form\Tests\Extension\Core\Type\TypeTestCase; - class FormTypeCsrfExtensionTest extends TypeTestCase { - protected $csrfProvider; - - protected function setUp() - { - $this->csrfProvider = $this->getMock('Symfony\Component\Form\Extension\Csrf\CsrfProvider\CsrfProviderInterface'); - - parent::setUp(); - } - - protected function tearDown() - { - $this->csrfProvider = null; - - parent::tearDown(); - } - - protected function getExtensions() + public function testCsrfProtectionByDefault() { - return array_merge(parent::getExtensions(), array( - new CsrfExtension($this->csrfProvider), + $form = $this->factory->create('form', null, array( + 'csrf_field_name' => 'csrf', )); - } - public function testCsrfProtectionByDefaultIfRootAndChildren() - { - $view = $this->factory - ->createBuilder('form', null, array( - 'csrf_field_name' => 'csrf', - )) - ->add($this->factory->createNamedBuilder('form', 'child')) - ->getForm() - ->createView(); - - $this->assertTrue($view->hasChild('csrf')); - } - - public function testNoCsrfProtectionByDefaultIfChildrenButNotRoot() - { - $view = $this->factory - ->createNamedBuilder('form', 'root') - ->add($this->factory - ->createNamedBuilder('form', 'form', null, array( - 'csrf_field_name' => 'csrf', - )) - ->add($this->factory->createNamedBuilder('form', 'child')) - ) - ->getForm() - ->get('form') - ->createView(); - - $this->assertFalse($view->hasChild('csrf')); - } - - public function testNoCsrfProtectionByDefaultIfRootButNoChildren() - { - $view = $this->factory - ->createBuilder('form', null, array( - 'csrf_field_name' => 'csrf', - )) - ->getForm() - ->createView(); - - $this->assertFalse($view->hasChild('csrf')); + $this->assertTrue($form->has('csrf')); } public function testCsrfProtectionCanBeDisabled() { - $view = $this->factory - ->createBuilder('form', null, array( - 'csrf_field_name' => 'csrf', - 'csrf_protection' => false, - )) - ->add($this->factory->createNamedBuilder('form', 'child')) - ->getForm() - ->createView(); - - $this->assertFalse($view->hasChild('csrf')); - } - - public function testGenerateCsrfToken() - { - $this->csrfProvider->expects($this->once()) - ->method('generateCsrfToken') - ->with('%INTENTION%') - ->will($this->returnValue('token')); - - $view = $this->factory - ->createBuilder('form', null, array( - 'csrf_field_name' => 'csrf', - 'csrf_provider' => $this->csrfProvider, - 'intention' => '%INTENTION%' - )) - ->add($this->factory->createNamedBuilder('form', 'child')) - ->getForm() - ->createView(); - - $this->assertEquals('token', $view->getChild('csrf')->get('value')); - } + $form = $this->factory->create('form', null, array( + 'csrf_protection' => false, + )); - public function provideBoolean() - { - return array( - array(true), - array(false), - ); + $this->assertCount(0, $form); } - /** - * @dataProvider provideBoolean - */ - public function testValidateTokenOnBindIfRootAndChildren($valid) + public function testCsrfTokenIsOnlyIncludedInRootView() { - $this->csrfProvider->expects($this->once()) - ->method('isCsrfTokenValid') - ->with('%INTENTION%', 'token') - ->will($this->returnValue($valid)); - - $form = $this->factory - ->createBuilder('form', null, array( + $view = + $this->factory->createBuilder('form', null, array( 'csrf_field_name' => 'csrf', - 'csrf_provider' => $this->csrfProvider, - 'intention' => '%INTENTION%' )) - ->add($this->factory->createNamedBuilder('form', 'child')) - ->getForm(); - - $form->bind(array( - 'child' => 'foobar', - 'csrf' => 'token', - )); - - // Remove token from data - $this->assertSame(array('child' => 'foobar'), $form->getData()); - - // Validate accordingly - $this->assertSame($valid, $form->isValid()); - } - - public function testDontValidateTokenIfChildrenButNoRoot() - { - $this->csrfProvider->expects($this->never()) - ->method('isCsrfTokenValid'); - - $form = $this->factory - ->createNamedBuilder('form', 'root') - ->add($this->factory - ->createNamedBuilder('form', 'form', null, array( + ->add('notCsrf', 'text') + ->add( + $this->factory->createNamedBuilder('form', 'child', null, array( 'csrf_field_name' => 'csrf', - 'csrf_provider' => $this->csrfProvider, - 'intention' => '%INTENTION%' )) - ->add($this->factory->createNamedBuilder('form', 'child')) + ->add('notCsrf', 'text') ) ->getForm() - ->get('form'); - - $form->bind(array( - 'child' => 'foobar', - 'csrf' => 'token', - )); - } - - public function testDontValidateTokenIfRootButNoChildren() - { - $this->csrfProvider->expects($this->never()) - ->method('isCsrfTokenValid'); - - $form = $this->factory - ->createBuilder('form', null, array( - 'csrf_field_name' => 'csrf', - 'csrf_provider' => $this->csrfProvider, - 'intention' => '%INTENTION%' - )) - ->getForm(); + ->createView(); - $form->bind(array( - 'csrf' => 'token', - )); + $this->assertEquals(array('csrf', 'notCsrf', 'child'), array_keys(iterator_to_array($view))); + $this->assertEquals(array('notCsrf'), array_keys(iterator_to_array($view['child']))); } } diff --git a/src/Symfony/Component/Form/Tests/Extension/Csrf/Type/TypeTestCase.php b/src/Symfony/Component/Form/Tests/Extension/Csrf/Type/TypeTestCase.php new file mode 100644 index 0000000000000..3b1ce91783ca7 --- /dev/null +++ b/src/Symfony/Component/Form/Tests/Extension/Csrf/Type/TypeTestCase.php @@ -0,0 +1,41 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Extension\Csrf\Type; + +use Symfony\Component\Form\Tests\Extension\Core\Type\TypeTestCase as BaseTestCase; +use Symfony\Component\Form\Extension\Csrf\CsrfExtension; + +abstract class TypeTestCase extends BaseTestCase +{ + protected $csrfProvider; + + protected function setUp() + { + $this->csrfProvider = $this->getMock('Symfony\Component\Form\Extension\Csrf\CsrfProvider\CsrfProviderInterface'); + + parent::setUp(); + } + + protected function tearDown() + { + $this->csrfProvider = null; + + parent::tearDown(); + } + + protected function getExtensions() + { + return array_merge(parent::getExtensions(), array( + new CsrfExtension($this->csrfProvider), + )); + } +} diff --git a/src/Symfony/Component/Form/Tests/Extension/Validator/EventListener/DelegatingValidationListenerTest.php b/src/Symfony/Component/Form/Tests/Extension/Validator/EventListener/DelegatingValidationListenerTest.php index 187ce1a2b7bbe..0071b36fd728a 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Validator/EventListener/DelegatingValidationListenerTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Validator/EventListener/DelegatingValidationListenerTest.php @@ -93,7 +93,6 @@ protected function getBuilder($name = 'name', $propertyPath = null) $builder = new FormBuilder($name, $this->factory, $this->dispatcher); $builder->setAttribute('property_path', new PropertyPath($propertyPath ?: $name)); $builder->setAttribute('error_mapping', array()); - $builder->setErrorBubbling(false); return $builder; } diff --git a/src/Symfony/Component/Form/Tests/Extension/Validator/Type/FormTypeValidatorExtensionTest.php b/src/Symfony/Component/Form/Tests/Extension/Validator/Type/FieldTypeValidatorExtensionTest.php similarity index 79% rename from src/Symfony/Component/Form/Tests/Extension/Validator/Type/FormTypeValidatorExtensionTest.php rename to src/Symfony/Component/Form/Tests/Extension/Validator/Type/FieldTypeValidatorExtensionTest.php index 5cd0ea753b8d1..fdf011d66a7e2 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Validator/Type/FormTypeValidatorExtensionTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Validator/Type/FieldTypeValidatorExtensionTest.php @@ -13,18 +13,18 @@ use Symfony\Component\Form\FormInterface; -class FormTypeValidatorExtensionTest extends TypeTestCase +class FieldTypeValidatorExtensionTest extends TypeTestCase { public function testValidationGroupNullByDefault() { - $form = $this->factory->create('form'); + $form = $this->factory->create('field'); $this->assertNull($form->getAttribute('validation_groups')); } public function testValidationGroupsCanBeSetToString() { - $form = $this->factory->create('form', null, array( + $form = $this->factory->create('field', null, array( 'validation_groups' => 'group', )); @@ -33,7 +33,7 @@ public function testValidationGroupsCanBeSetToString() public function testValidationGroupsCanBeSetToArray() { - $form = $this->factory->create('form', null, array( + $form = $this->factory->create('field', null, array( 'validation_groups' => array('group1', 'group2'), )); @@ -42,7 +42,7 @@ public function testValidationGroupsCanBeSetToArray() public function testValidationGroupsCanBeSetToCallback() { - $form = $this->factory->create('form', null, array( + $form = $this->factory->create('field', null, array( 'validation_groups' => array($this, 'testValidationGroupsCanBeSetToCallback'), )); @@ -51,7 +51,7 @@ public function testValidationGroupsCanBeSetToCallback() public function testValidationGroupsCanBeSetToClosure() { - $form = $this->factory->create('form', null, array( + $form = $this->factory->create('field', null, array( 'validation_groups' => function(FormInterface $form){ return null; }, )); @@ -60,10 +60,10 @@ public function testValidationGroupsCanBeSetToClosure() public function testBindValidatesData() { - $builder = $this->factory->createBuilder('form', null, array( + $builder = $this->factory->createBuilder('field', null, array( 'validation_groups' => 'group', )); - $builder->add('firstName', 'form'); + $builder->add('firstName', 'field'); $form = $builder->getForm(); $this->validator->expects($this->once()) diff --git a/src/Symfony/Component/Form/Tests/FormBuilderTest.php b/src/Symfony/Component/Form/Tests/FormBuilderTest.php index bbae7ee2dd881..f5276f322e518 100644 --- a/src/Symfony/Component/Form/Tests/FormBuilderTest.php +++ b/src/Symfony/Component/Form/Tests/FormBuilderTest.php @@ -71,7 +71,7 @@ public function testConstructAcceptsOnlyNamesValidAsIdsInHtml4($name, $accepted) * Changing the name is not allowed, otherwise the name and property path * are not synchronized anymore * - * @see FormType::buildForm + * @see FieldType::buildForm */ public function testNoSetName() { diff --git a/src/Symfony/Component/Form/Tests/FormFactoryTest.php b/src/Symfony/Component/Form/Tests/FormFactoryTest.php index 206fa5eccf396..057e9b01f9933 100644 --- a/src/Symfony/Component/Form/Tests/FormFactoryTest.php +++ b/src/Symfony/Component/Form/Tests/FormFactoryTest.php @@ -346,7 +346,7 @@ public function testCreateUsesTypeNameAsName() $this->assertEquals('foo', $builder->getName()); } - public function testCreateBuilderForPropertyCreatesFormWithHighestConfidence() + public function testCreateBuilderForPropertyCreatesFieldWithHighestConfidence() { $this->guesser1->expects($this->once()) ->method('guessType') @@ -378,7 +378,7 @@ public function testCreateBuilderForPropertyCreatesFormWithHighestConfidence() $this->assertEquals('builderInstance', $builder); } - public function testCreateBuilderCreatesTextFormIfNoGuess() + public function testCreateBuilderCreatesTextFieldIfNoGuess() { $this->guesser1->expects($this->once()) ->method('guessType') diff --git a/src/Symfony/Component/Form/Tests/FormTest.php b/src/Symfony/Component/Form/Tests/FormTest.php index 08ef363e4bcf4..a5c20dcbcfb14 100644 --- a/src/Symfony/Component/Form/Tests/FormTest.php +++ b/src/Symfony/Component/Form/Tests/FormTest.php @@ -159,35 +159,6 @@ public function testErrorsDontBubbleUpIfDisabled() $this->assertEquals(array(), $parent->getErrors()); } - public function testErrorsBubbleUpIfNullAndChildren() - { - $error = new FormError('Error!'); - $parent = $this->form; - $form = $this->getBuilder() - ->setErrorBubbling(null) - ->add($this->getBuilder('child')) - ->getForm(); - - $form->setParent($parent); - $form->addError($error); - - $this->assertEquals(array(), $form->getErrors()); - $this->assertEquals(array($error), $parent->getErrors()); - } - - public function testErrorsDontBubbleUpIfNullAndNoChildren() - { - $error = new FormError('Error!'); - $parent = $this->form; - $form = $this->getBuilder()->setErrorBubbling(null)->getForm(); - - $form->setParent($parent); - $form->addError($error); - - $this->assertEquals(array($error), $form->getErrors()); - $this->assertEquals(array(), $parent->getErrors()); - } - public function testValidIfAllChildrenAreValid() { $this->form->add($this->getValidForm('firstName')); @@ -1055,7 +1026,7 @@ public function testBindPostOrPutRequestWithEmptyRootFormName($method) /** * @dataProvider requestMethodProvider */ - public function testBindPostOrPutRequestWithSingleChildForm($method) + public function testBindPostOrPutRequestWithSingleFieldForm($method) { if (!class_exists('Symfony\Component\HttpFoundation\Request')) { $this->markTestSkipped('The "HttpFoundation" component is not available'); @@ -1092,7 +1063,7 @@ public function testBindPostOrPutRequestWithSingleChildForm($method) /** * @dataProvider requestMethodProvider */ - public function testBindPostOrPutRequestWithSingleChildFormUploadedFile($method) + public function testBindPostOrPutRequestWithSingleFieldFormUploadedFile($method) { if (!class_exists('Symfony\Component\HttpFoundation\Request')) { $this->markTestSkipped('The "HttpFoundation" component is not available'); @@ -1276,7 +1247,7 @@ public function testCreateView() public function testCreateViewAcceptsParent() { - $parent = new FormView('form'); + $parent = new FormView(); $form = $this->getBuilder()->getForm(); $view = $form->createView($parent); From 1c7c99d97cea4fa9f0b6c2948e83c418bdca4f85 Mon Sep 17 00:00:00 2001 From: Victor Berchet Date: Fri, 27 Apr 2012 13:01:06 +0200 Subject: [PATCH 4/8] [Form] Restore missing use statements --- .../Component/Form/Extension/Core/Type/FieldType.php | 9 +++++++++ .../Form/Extension/Csrf/Type/FormTypeCsrfExtension.php | 1 + 2 files changed, 10 insertions(+) diff --git a/src/Symfony/Component/Form/Extension/Core/Type/FieldType.php b/src/Symfony/Component/Form/Extension/Core/Type/FieldType.php index 74c9cb0327b67..6989e7227dcc2 100644 --- a/src/Symfony/Component/Form/Extension/Core/Type/FieldType.php +++ b/src/Symfony/Component/Form/Extension/Core/Type/FieldType.php @@ -13,6 +13,15 @@ use Symfony\Component\Form\AbstractType; use Symfony\Component\EventDispatcher\EventDispatcher; +use Symfony\Component\Form\FormBuilder; +use Symfony\Component\Form\FormView; +use Symfony\Component\Form\FormInterface; +use Symfony\Component\Form\FormFactoryInterface; +use Symfony\Component\Form\Options; +use Symfony\Component\Form\Util\PropertyPath; +use Symfony\Component\Form\Extension\Core\EventListener\ValidationListener; +use Symfony\Component\Form\Extension\Core\EventListener\TrimListener; +use Symfony\Component\Form\Exception\FormException; class FieldType extends AbstractType { diff --git a/src/Symfony/Component/Form/Extension/Csrf/Type/FormTypeCsrfExtension.php b/src/Symfony/Component/Form/Extension/Csrf/Type/FormTypeCsrfExtension.php index c248c3182e489..a0a51e439ca59 100644 --- a/src/Symfony/Component/Form/Extension/Csrf/Type/FormTypeCsrfExtension.php +++ b/src/Symfony/Component/Form/Extension/Csrf/Type/FormTypeCsrfExtension.php @@ -16,6 +16,7 @@ use Symfony\Component\Form\FormBuilder; use Symfony\Component\Form\FormView; use Symfony\Component\Form\FormInterface; +use Symfony\Component\Form\FormEvents; class FormTypeCsrfExtension extends AbstractTypeExtension { From 43604e22b5d50564ddbb78e92a4030ee727d5904 Mon Sep 17 00:00:00 2001 From: Victor Berchet Date: Fri, 27 Apr 2012 13:06:17 +0200 Subject: [PATCH 5/8] [Form] Revert "merged branch helmer/readonly_fix (PR #3258)" This reverts commit 52511770027c84a4ff38851c08c8d61f88df6db2, reversing changes made to ffef177c4a6474d284f01c4336a874d8840be9c0. --- .../Form/Extension/Core/Type/ChoiceType.php | 24 ++++++++--------- .../Form/Extension/Core/Type/FieldType.php | 6 +---- src/Symfony/Component/Form/FormBuilder.php | 2 +- .../Extension/Core/Type/FieldTypeTest.php | 27 ------------------- 4 files changed, 13 insertions(+), 46 deletions(-) diff --git a/src/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php b/src/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php index 1ed495248bb61..385a2bb32c1e3 100644 --- a/src/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php +++ b/src/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php @@ -206,23 +206,21 @@ private function addSubFields(FormBuilder $builder, array $choiceViews, array $o if (is_array($choiceView)) { // Flatten groups $this->addSubFields($builder, $choiceView, $options); - } else { - $choiceOpts = array( + } elseif ($options['multiple']) { + $builder->add((string) $i, 'checkbox', array( 'value' => $choiceView->getValue(), 'label' => $choiceView->getLabel(), - 'translation_domain' => $options['translation_domain'], - ); - - if ($options['multiple']) { - $choiceType = 'checkbox'; // The user can check 0 or more checkboxes. If required // is true, he is required to check all of them. - $choiceOpts['required'] = false; - } else { - $choiceType = 'radio'; - } - - $builder->add((string) $i, $choiceType, $choiceOpts); + 'required' => false, + 'translation_domain' => $options['translation_domain'], + )); + } else { + $builder->add((string) $i, 'radio', array( + 'value' => $choiceView->getValue(), + 'label' => $choiceView->getLabel(), + 'translation_domain' => $options['translation_domain'], + )); } } } diff --git a/src/Symfony/Component/Form/Extension/Core/Type/FieldType.php b/src/Symfony/Component/Form/Extension/Core/Type/FieldType.php index 6989e7227dcc2..82896259d4d86 100644 --- a/src/Symfony/Component/Form/Extension/Core/Type/FieldType.php +++ b/src/Symfony/Component/Form/Extension/Core/Type/FieldType.php @@ -74,7 +74,6 @@ public function buildForm(FormBuilder $builder, array $options) public function buildView(FormView $view, FormInterface $form) { $name = $form->getName(); - $readOnly = $form->getAttribute('read_only'); if ($view->hasParent()) { if ('' === $name) { @@ -88,9 +87,6 @@ public function buildView(FormView $view, FormInterface $form) $id = $name; $fullName = $name; } - - // Complex fields are read-only if themselves or their parent is. - $readOnly = $readOnly || $view->getParent()->get('read_only'); } else { $id = $name; $fullName = $name; @@ -111,9 +107,9 @@ public function buildView(FormView $view, FormInterface $form) ->set('id', $id) ->set('name', $name) ->set('full_name', $fullName) - ->set('read_only', $readOnly) ->set('errors', $form->getErrors()) ->set('value', $form->getClientData()) + ->set('read_only', $form->getAttribute('read_only')) ->set('disabled', $form->isDisabled()) ->set('required', $form->isRequired()) ->set('max_length', $form->getAttribute('max_length')) diff --git a/src/Symfony/Component/Form/FormBuilder.php b/src/Symfony/Component/Form/FormBuilder.php index 208a518518b41..ce7be0a56eac9 100644 --- a/src/Symfony/Component/Form/FormBuilder.php +++ b/src/Symfony/Component/Form/FormBuilder.php @@ -187,7 +187,7 @@ public function getData() } /** - * Set whether the form is disabled. + * Set whether the form is disabled * * @param Boolean $disabled Whether the form is disabled * diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/Type/FieldTypeTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/Type/FieldTypeTest.php index b588d4433f250..3844c8cc92f99 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Core/Type/FieldTypeTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Core/Type/FieldTypeTest.php @@ -144,33 +144,6 @@ public function testPassIdAndNameToViewWithGrandParent() $this->assertEquals('parent[child][grand_child]', $view['child']['grand_child']->get('full_name')); } - public function testNonReadOnlyFieldWithReadOnlyParentBeingReadOnly() - { - $parent = $this->factory->createNamed('field', 'parent', null, array('read_only' => true)); - $child = $this->factory->createNamed('field', 'child'); - $view = $parent->add($child)->createView(); - - $this->assertTrue($view['child']->get('read_only')); - } - - public function testReadOnlyFieldWithNonReadOnlyParentBeingReadOnly() - { - $parent = $this->factory->createNamed('field', 'parent'); - $child = $this->factory->createNamed('field', 'child', null, array('read_only' => true)); - $view = $parent->add($child)->createView(); - - $this->assertTrue($view['child']->get('read_only')); - } - - public function testNonReadOnlyFieldWithNonReadOnlyParentBeingNonReadOnly() - { - $parent = $this->factory->createNamed('field', 'parent'); - $child = $this->factory->createNamed('field', 'child'); - $view = $parent->add($child)->createView(); - - $this->assertFalse($view['child']->get('read_only')); - } - public function testPassMaxLengthToView() { $form = $this->factory->create('field', null, array('max_length' => 10)); From ccd8b67b78a7cfbaa720fe9101414af2b2b438ab Mon Sep 17 00:00:00 2001 From: Victor Berchet Date: Fri, 27 Apr 2012 13:16:34 +0200 Subject: [PATCH 6/8] [Form] Revert "merged branch helmer/readonly (PR #3193)" This reverts commit e71d1579d1c8404117c0a8b8a015b5482a6bb66b, reversing changes made to 253a8ff0163a9d54cc299d93d7382de6bf5d1893. Conflicts: src/Symfony/Component/Form/Form.php src/Symfony/Component/Form/Tests/FormFactoryTest.php --- .../views/Form/form_div_layout.html.twig | 2 +- .../Resources/views/Form/attributes.html.php | 3 +- .../Form/Extension/Core/Type/FieldType.php | 7 ++-- src/Symfony/Component/Form/Form.php | 31 +++++++++++----- src/Symfony/Component/Form/FormBuilder.php | 20 +++++------ src/Symfony/Component/Form/FormInterface.php | 10 +++--- .../Form/Tests/AbstractLayoutTest.php | 30 ---------------- .../Extension/Core/Type/FieldTypeTest.php | 6 ++-- .../Component/Form/Tests/FormFactoryTest.php | 4 +-- src/Symfony/Component/Form/Tests/FormTest.php | 36 +++++++++---------- 10 files changed, 64 insertions(+), 85 deletions(-) diff --git a/src/Symfony/Bridge/Twig/Resources/views/Form/form_div_layout.html.twig b/src/Symfony/Bridge/Twig/Resources/views/Form/form_div_layout.html.twig index cc3f3fd01d688..556f4193ccebf 100644 --- a/src/Symfony/Bridge/Twig/Resources/views/Form/form_div_layout.html.twig +++ b/src/Symfony/Bridge/Twig/Resources/views/Form/form_div_layout.html.twig @@ -295,7 +295,7 @@ {% block widget_attributes %} {% spaceless %} - id="{{ id }}" name="{{ full_name }}"{% if read_only %} readonly="readonly"{% endif %}{% if disabled %} disabled="disabled"{% endif %}{% if required %} required="required"{% endif %}{% if max_length %} maxlength="{{ max_length }}"{% endif %}{% if pattern %} pattern="{{ pattern }}"{% endif %} + id="{{ id }}" name="{{ full_name }}"{% if read_only %} disabled="disabled"{% endif %}{% if required %} required="required"{% endif %}{% if max_length %} maxlength="{{ max_length }}"{% endif %}{% if pattern %} pattern="{{ pattern }}"{% endif %} {% for attrname,attrvalue in attr %}{{attrname}}="{{attrvalue}}" {% endfor %} {% endspaceless %} {% endblock widget_attributes %} diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/attributes.html.php b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/attributes.html.php index fe4afa01d2e61..33b3c7eac1a71 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/attributes.html.php +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/attributes.html.php @@ -1,7 +1,6 @@ id="escape($id) ?>" name="escape($full_name) ?>" -readonly="readonly" -disabled="disabled" +disabled="disabled" required="required" maxlength="escape($max_length) ?>" pattern="escape($pattern) ?>" diff --git a/src/Symfony/Component/Form/Extension/Core/Type/FieldType.php b/src/Symfony/Component/Form/Extension/Core/Type/FieldType.php index 82896259d4d86..ee85816b7041a 100644 --- a/src/Symfony/Component/Form/Extension/Core/Type/FieldType.php +++ b/src/Symfony/Component/Form/Extension/Core/Type/FieldType.php @@ -45,10 +45,9 @@ public function buildForm(FormBuilder $builder, array $options) $builder ->setRequired($options['required']) - ->setDisabled($options['disabled']) + ->setReadOnly($options['read_only']) ->setErrorBubbling($options['error_bubbling']) ->setEmptyData($options['empty_data']) - ->setAttribute('read_only', $options['read_only']) ->setAttribute('by_reference', $options['by_reference']) ->setAttribute('property_path', $options['property_path']) ->setAttribute('error_mapping', $options['error_mapping']) @@ -109,8 +108,7 @@ public function buildView(FormView $view, FormInterface $form) ->set('full_name', $fullName) ->set('errors', $form->getErrors()) ->set('value', $form->getClientData()) - ->set('read_only', $form->getAttribute('read_only')) - ->set('disabled', $form->isDisabled()) + ->set('read_only', $form->isReadOnly()) ->set('required', $form->isRequired()) ->set('max_length', $form->getAttribute('max_length')) ->set('pattern', $form->getAttribute('pattern')) @@ -161,7 +159,6 @@ public function getDefaultOptions() 'trim' => true, 'required' => true, 'read_only' => false, - 'disabled' => false, 'max_length' => null, 'pattern' => null, 'property_path' => null, diff --git a/src/Symfony/Component/Form/Form.php b/src/Symfony/Component/Form/Form.php index 8f9e7b6de2403..3cd6e298931fa 100644 --- a/src/Symfony/Component/Form/Form.php +++ b/src/Symfony/Component/Form/Form.php @@ -167,7 +167,7 @@ class Form implements \IteratorAggregate, FormInterface * Whether this form may only be read, but not bound * @var Boolean */ - private $disabled = false; + private $readOnly = false; /** * The dispatcher for distributing events of this form @@ -191,7 +191,7 @@ public function __construct($name, EventDispatcherInterface $dispatcher, array $types = array(), array $clientTransformers = array(), array $normTransformers = array(), DataMapperInterface $dataMapper = null, array $validators = array(), - $required = false, $disabled = false, $errorBubbling = false, + $required = false, $readOnly = false, $errorBubbling = false, $emptyData = null, array $attributes = array()) { $name = (string) $name; @@ -224,7 +224,7 @@ public function __construct($name, EventDispatcherInterface $dispatcher, $this->dataMapper = $dataMapper; $this->validators = $validators; $this->required = (Boolean) $required; - $this->disabled = (Boolean) $disabled; + $this->readOnly = (Boolean) $readOnly; $this->errorBubbling = (Boolean) $errorBubbling; $this->emptyData = $emptyData; $this->attributes = $attributes; @@ -271,6 +271,7 @@ public function getTypes() public function isRequired() { if (null === $this->parent || $this->parent->isRequired()) { + return $this->required; } @@ -278,12 +279,21 @@ public function isRequired() } /** - * {@inheritDoc} + * Returns whether this form is read only. + * + * The content of a read-only form is displayed, but not allowed to be + * modified. The validation of modified read-only forms should fail. + * + * Fields whose parents are read-only are considered read-only regardless of + * their own state. + * + * @return Boolean */ - public function isDisabled() + public function isReadOnly() { - if (null === $this->parent || !$this->parent->isDisabled()) { - return $this->disabled; + if (null === $this->parent || !$this->parent->isReadOnly()) { + + return $this->readOnly; } return true; @@ -464,7 +474,7 @@ public function bind($clientData) throw new AlreadyBoundException('A form can only be bound once'); } - if ($this->isDisabled()) { + if ($this->readOnly) { $this->bound = true; return $this; @@ -694,6 +704,7 @@ public function isEmpty() { foreach ($this->children as $child) { if (!$child->isEmpty()) { + return false; } } @@ -716,9 +727,10 @@ public function isValid() return false; } - if (!$this->isDisabled()) { + if (!$this->readOnly) { foreach ($this->children as $child) { if (!$child->isValid()) { + return false; } } @@ -891,6 +903,7 @@ public function has($name) public function get($name) { if (isset($this->children[$name])) { + return $this->children[$name]; } diff --git a/src/Symfony/Component/Form/FormBuilder.php b/src/Symfony/Component/Form/FormBuilder.php index ce7be0a56eac9..ab014b66fe84b 100644 --- a/src/Symfony/Component/Form/FormBuilder.php +++ b/src/Symfony/Component/Form/FormBuilder.php @@ -49,7 +49,7 @@ class FormBuilder /** * @var Boolean */ - private $disabled; + private $readOnly; /** * @var Boolean @@ -187,27 +187,27 @@ public function getData() } /** - * Set whether the form is disabled + * Set whether the form is read only * - * @param Boolean $disabled Whether the form is disabled + * @param Boolean $readOnly Whether the form is read only * * @return FormBuilder The current builder */ - public function setDisabled($disabled) + public function setReadOnly($readOnly) { - $this->disabled = (Boolean) $disabled; + $this->readOnly = (Boolean) $readOnly; return $this; } /** - * Returns whether the form is disabled. + * Returns whether the form is read only. * - * @return Boolean Whether the form is disabled + * @return Boolean Whether the form is read only */ - public function getDisabled() + public function getReadOnly() { - return $this->disabled; + return $this->readOnly; } /** @@ -676,7 +676,7 @@ public function getForm() $this->getDataMapper(), $this->getValidators(), $this->getRequired(), - $this->getDisabled(), + $this->getReadOnly(), $this->getErrorBubbling(), $this->getEmptyData(), $this->getAttributes() diff --git a/src/Symfony/Component/Form/FormInterface.php b/src/Symfony/Component/Form/FormInterface.php index d0d8a80a8d127..c0d6eac12152f 100644 --- a/src/Symfony/Component/Form/FormInterface.php +++ b/src/Symfony/Component/Form/FormInterface.php @@ -178,17 +178,17 @@ function isValid(); function isRequired(); /** - * Returns whether this form is disabled. + * Returns whether this form can be read only. * - * The content of a disabled form is displayed, but not allowed to be - * modified. The validation of modified disabled forms should fail. + * The content of a read-only form is displayed, but not allowed to be + * modified. The validation of modified read-only forms should fail. * - * Fields whose parents are disabled are considered disabled regardless of + * Fields whose parents are read-only are considered read-only regardless of * their own state. * * @return Boolean */ - function isDisabled(); + function isReadOnly(); /** * Returns whether the form is empty. diff --git a/src/Symfony/Component/Form/Tests/AbstractLayoutTest.php b/src/Symfony/Component/Form/Tests/AbstractLayoutTest.php index 32aabb86a6707..4afb361e012f1 100644 --- a/src/Symfony/Component/Form/Tests/AbstractLayoutTest.php +++ b/src/Symfony/Component/Form/Tests/AbstractLayoutTest.php @@ -1231,36 +1231,6 @@ public function testHidden() ); } - public function testReadOnly() - { - $form = $this->factory->createNamed('text', 'name', null, array( - 'read_only' => true, - )); - - $this->assertWidgetMatchesXpath($form->createView(), array(), -'/input - [@type="text"] - [@name="name"] - [@readonly="readonly"] -' - ); - } - - public function testDisabled() - { - $form = $this->factory->createNamed('text', 'name', null, array( - 'disabled' => true, - )); - - $this->assertWidgetMatchesXpath($form->createView(), array(), -'/input - [@type="text"] - [@name="name"] - [@disabled="disabled"] -' - ); - } - public function testInteger() { $form = $this->factory->createNamed('integer', 'name', 123); diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/Type/FieldTypeTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/Type/FieldTypeTest.php index 3844c8cc92f99..6f4a4be30fb7a 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Core/Type/FieldTypeTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Core/Type/FieldTypeTest.php @@ -64,11 +64,11 @@ public function testPassRequiredAsOption() $this->assertTrue($form->isRequired()); } - public function testPassDisabledAsOption() + public function testPassReadOnlyAsOption() { - $form = $this->factory->create('field', null, array('disabled' => true)); + $form = $this->factory->create('field', null, array('read_only' => true)); - $this->assertTrue($form->isDisabled()); + $this->assertTrue($form->isReadOnly()); } public function testBoundDataIsTrimmedBeforeTransforming() diff --git a/src/Symfony/Component/Form/Tests/FormFactoryTest.php b/src/Symfony/Component/Form/Tests/FormFactoryTest.php index 057e9b01f9933..cbf67d9bfb9fa 100644 --- a/src/Symfony/Component/Form/Tests/FormFactoryTest.php +++ b/src/Symfony/Component/Form/Tests/FormFactoryTest.php @@ -616,7 +616,7 @@ public function testUnknownOptions() $this->setExpectedException('Symfony\Component\Form\Exception\InvalidOptionException', 'The options "invalid", "unknown" do not exist. Known options are: ' . - '"attr", "by_reference", "data", "data_class", "disabled", ' . + '"attr", "by_reference", "data", "data_class", ' . '"empty_data", "error_bubbling", "error_mapping", "invalid_message", ' . '"invalid_message_parameters", "label", "max_length", "pattern", ' . '"property_path", "read_only", "required", "translation_domain", ' . @@ -633,7 +633,7 @@ public function testUnknownOption() $this->setExpectedException('Symfony\Component\Form\Exception\InvalidOptionException', 'The option "unknown" does not exist. Known options are: "attr", ' . - '"by_reference", "data", "data_class", "disabled", "empty_data", ' . + '"by_reference", "data", "data_class", "empty_data", ' . '"error_bubbling", "error_mapping", "invalid_message", ' . '"invalid_message_parameters", "label", "max_length", "pattern", ' . '"property_path", "read_only", "required", "translation_domain", ' . diff --git a/src/Symfony/Component/Form/Tests/FormTest.php b/src/Symfony/Component/Form/Tests/FormTest.php index a5c20dcbcfb14..530ab715d5faf 100644 --- a/src/Symfony/Component/Form/Tests/FormTest.php +++ b/src/Symfony/Component/Form/Tests/FormTest.php @@ -222,10 +222,10 @@ public function testBindForwardsNullIfValueIsMissing() $this->form->bind(array()); } - public function testBindIsIgnoredIfDisabled() + public function testBindIsIgnoredIfReadOnly() { $form = $this->getBuilder() - ->setDisabled(true) + ->setReadOnly(true) ->setData('initial') ->getForm(); @@ -265,34 +265,34 @@ public function testNotRequired() $this->assertFalse($child->isRequired()); } - public function testAlwaysDisabledIfParentDisabled() + public function testAlwaysReadOnlyIfParentReadOnly() { - $parent = $this->getBuilder()->setDisabled(true)->getForm(); - $child = $this->getBuilder()->setDisabled(false)->getForm(); + $parent = $this->getBuilder()->setReadOnly(true)->getForm(); + $child = $this->getBuilder()->setReadOnly(false)->getForm(); $child->setParent($parent); - $this->assertTrue($child->isDisabled()); + $this->assertTrue($child->isReadOnly()); } - public function testDisabled() + public function testReadOnly() { - $parent = $this->getBuilder()->setDisabled(false)->getForm(); - $child = $this->getBuilder()->setDisabled(true)->getForm(); + $parent = $this->getBuilder()->setReadOnly(false)->getForm(); + $child = $this->getBuilder()->setReadOnly(true)->getForm(); $child->setParent($parent); - $this->assertTrue($child->isDisabled()); + $this->assertTrue($child->isReadOnly()); } - public function testNotDisabled() + public function testNotReadOnly() { - $parent = $this->getBuilder()->setDisabled(false)->getForm(); - $child = $this->getBuilder()->setDisabled(false)->getForm(); + $parent = $this->getBuilder()->setReadOnly(false)->getForm(); + $child = $this->getBuilder()->setReadOnly(false)->getForm(); $child->setParent($parent); - $this->assertFalse($child->isDisabled()); + $this->assertFalse($child->isReadOnly()); } public function testCloneChildren() @@ -371,15 +371,15 @@ public function testValidIfBound() $this->assertTrue($this->form->isValid()); } - public function testValidIfBoundAndDisabled() + public function testValidIfBoundAndReadOnly() { - $form = $this->getBuilder()->setDisabled(true)->getForm(); + $form = $this->getBuilder()->setReadOnly(true)->getForm(); $form->bind('foobar'); $this->assertTrue($form->isValid()); } - public function testValidIfBoundAndDisabledWithChildren() + public function testValidIfBoundAndReadOnlyWithChildren() { $this->factory->expects($this->once()) ->method('createNamedBuilder') @@ -387,7 +387,7 @@ public function testValidIfBoundAndDisabledWithChildren() ->will($this->returnValue($this->getBuilder('name'))); $form = $this->getBuilder('person') - ->setDisabled(true) + ->setReadOnly(true) ->add('name', 'text') ->getForm(); $form->bind(array('name' => 'Jacques Doe')); From 2440349d4c54f185a38ad1512d5855bb1c1a1084 Mon Sep 17 00:00:00 2001 From: Victor Berchet Date: Fri, 27 Apr 2012 13:23:09 +0200 Subject: [PATCH 7/8] [Form] Revert "merged branch stloyd/missingClientTransformer (PR #2421)" This reverts commit 7ea9c5b92adaf1224eec99259c3c8bd5f0ba73ba, reversing changes made to 5803146a9e50b5f0283a7505a7e939cc2b0b4eaf. Conflicts: src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/ValueToStringTransformerTest.php --- .../ValueToStringTransformer.php | 67 -------------- .../Form/Extension/Core/Type/TextType.php | 12 --- .../ValueToStringTransformerTest.php | 90 ------------------- .../Tests/Extension/Core/Type/UrlTypeTest.php | 2 +- 4 files changed, 1 insertion(+), 170 deletions(-) delete mode 100644 src/Symfony/Component/Form/Extension/Core/DataTransformer/ValueToStringTransformer.php delete mode 100644 src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/ValueToStringTransformerTest.php diff --git a/src/Symfony/Component/Form/Extension/Core/DataTransformer/ValueToStringTransformer.php b/src/Symfony/Component/Form/Extension/Core/DataTransformer/ValueToStringTransformer.php deleted file mode 100644 index c9538f403208c..0000000000000 --- a/src/Symfony/Component/Form/Extension/Core/DataTransformer/ValueToStringTransformer.php +++ /dev/null @@ -1,67 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Form\Extension\Core\DataTransformer; - -use Symfony\Component\Form\DataTransformerInterface; -use Symfony\Component\Form\Exception\UnexpectedTypeException; - -/** - * Transforms between a given value and a string. - * - * @author Joseph Bielawski - */ -class ValueToStringTransformer implements DataTransformerInterface -{ - /** - * Transforms a value into a string. - * - * @param mixed $value Mixed value. - * - * @return string String value. - * - * @throws UnexpectedTypeException if the given value is not a string or number - */ - public function transform($value) - { - if (null === $value) { - return ''; - } - - if (!is_string($value) && !is_numeric($value)) { - throw new UnexpectedTypeException($value, 'string or number'); - } - - return $value; - } - - /** - * Transforms a value into a string. - * - * @param string $value String value. - * - * @return string String value. - * - * @throws UnexpectedTypeException if the given value is not a string - */ - public function reverseTransform($value) - { - if (null === $value) { - return ''; - } - - if (!is_string($value)) { - throw new UnexpectedTypeException($value, 'string'); - } - - return $value; - } -} diff --git a/src/Symfony/Component/Form/Extension/Core/Type/TextType.php b/src/Symfony/Component/Form/Extension/Core/Type/TextType.php index 28003119340d1..bceb6af7e3069 100644 --- a/src/Symfony/Component/Form/Extension/Core/Type/TextType.php +++ b/src/Symfony/Component/Form/Extension/Core/Type/TextType.php @@ -12,21 +12,9 @@ namespace Symfony\Component\Form\Extension\Core\Type; use Symfony\Component\Form\AbstractType; -use Symfony\Component\Form\FormBuilder; -use Symfony\Component\Form\Extension\Core\DataTransformer\ValueToStringTransformer; class TextType extends AbstractType { - /** - * {@inheritdoc} - */ - public function buildForm(FormBuilder $builder, array $options) - { - $builder - ->appendClientTransformer(new ValueToStringTransformer()) - ; - } - /** * {@inheritdoc} */ diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/ValueToStringTransformerTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/ValueToStringTransformerTest.php deleted file mode 100644 index 0abe70aa34b36..0000000000000 --- a/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/ValueToStringTransformerTest.php +++ /dev/null @@ -1,90 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Form\Tests\Extension\Core\DataTransformer; - -use Symfony\Component\Form\Extension\Core\DataTransformer\ValueToStringTransformer; - -class ValueToStringTransformerTest extends \PHPUnit_Framework_TestCase -{ - protected $transformer; - - protected function setUp() - { - $this->transformer = new ValueToStringTransformer(); - } - - protected function tearDown() - { - $this->transformer = null; - } - - /** - * @dataProvider validDataProvider - */ - public function testTransform($value, $transformed) - { - $this->assertEquals($transformed, $this->transformer->transform($value)); - } - - /** - * @dataProvider validDataProvider - */ - public function testReverseTransform($value, $transformed) - { - $this->assertEquals($transformed, $this->transformer->reverseTransform($transformed)); - } - - public function validDataProvider() - { - return array( - array('test', 'test'), - array('', null), - array(null, null), - - array(0, '0'), - array('0', '0'), - array(1, '1'), - array('123', '123'), - array(1.23, '1.23'), - ); - } - - /** - * @dataProvider invalidDataProvider - */ - public function testTransformExpectsStringOrNumber($value) - { - $this->setExpectedException('Symfony\Component\Form\Exception\UnexpectedTypeException'); - - $this->transformer->transform($value); - } - - /** - * @dataProvider invalidDataProvider - */ - public function testReverseTransformExpectsString($value) - { - $this->setExpectedException('Symfony\Component\Form\Exception\UnexpectedTypeException'); - - $this->transformer->reverseTransform($value); - } - - public function invalidDataProvider() - { - return array( - array(true), - array(false), - array(new \stdClass), - array(array()), - ); - } -} diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/Type/UrlTypeTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/Type/UrlTypeTest.php index 28730d5a44cc9..a09e19347f9df 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Core/Type/UrlTypeTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Core/Type/UrlTypeTest.php @@ -43,7 +43,7 @@ public function testSubmitAddsNoDefaultProtocolIfEmpty() $form->bind(''); - $this->assertSame('', $form->getData()); + $this->assertNull($form->getData()); $this->assertSame('', $form->getClientData()); } From 12b3a0535c235c07c49de6c970f9f9edfde361cb Mon Sep 17 00:00:00 2001 From: Victor Berchet Date: Fri, 27 Apr 2012 13:59:06 +0200 Subject: [PATCH 8/8] [Form] Updated the changelog in regards to the reverted changes This reverts commit e71d1579d1c8404117c0a8b8a015b5482a6bb66b, reversing changes made to 253a8ff0163a9d54cc299d93d7382de6bf5d1893. Conflicts: src/Symfony/Component/Form/Form.php src/Symfony/Component/Form/Tests/FormFactoryTest.php Conflicts: CHANGELOG-2.1.md --- src/Symfony/Component/Form/CHANGELOG.md | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/Symfony/Component/Form/CHANGELOG.md b/src/Symfony/Component/Form/CHANGELOG.md index f6f32633e5f1d..080fde616074f 100644 --- a/src/Symfony/Component/Form/CHANGELOG.md +++ b/src/Symfony/Component/Form/CHANGELOG.md @@ -4,7 +4,6 @@ CHANGELOG 2.1.0 ----- - * [BC BREAK] ``read_only`` field attribute now renders as ``readonly="readonly"``, use ``disabled`` instead * [BC BREAK] child forms now aren't validated anymore by default * made validation of form children configurable (new option: cascade_validation) * added support for validation groups as callbacks @@ -60,10 +59,6 @@ CHANGELOG don't receive an options array anymore. * deprecated FormValidatorInterface and substituted its implementations by event subscribers - * simplified CSRF protection and removed the csrf type - * deprecated FieldType and merged it into FormType - * [BC BREAK] renamed "field_*" theme blocks to "form_*" and "field_widget" to - "input" * ValidatorTypeGuesser now guesses "collection" for array type constraint * added method `guessPattern` to FormTypeGuesserInterface to guess which pattern to use in the HTML5 attribute "pattern" * deprecated method `guessMinLength` in favor of `guessPattern`