From e5cc7cd12648e403c8a87bae3a0ba90a7ead4101 Mon Sep 17 00:00:00 2001 From: Jules Pietri Date: Fri, 25 Mar 2016 22:44:16 +0100 Subject: [PATCH 1/4] [Form] deprecated `choice_attr` as nested arrays mapped by indexes --- UPGRADE-3.3.md | 49 +++ UPGRADE-4.0.md | 46 +++ src/Symfony/Bridge/Twig/composer.json | 2 +- src/Symfony/Component/Form/CHANGELOG.md | 1 + .../Factory/DefaultChoiceListFactory.php | 24 +- .../Form/Extension/Core/Type/ChoiceType.php | 2 +- .../Tests/AbstractBootstrap3LayoutTest.php | 288 +++++++++++++++++- .../Form/Tests/AbstractLayoutTest.php | 218 ++++++++++++- .../Factory/DefaultChoiceListFactoryTest.php | 62 +++- 9 files changed, 673 insertions(+), 19 deletions(-) diff --git a/UPGRADE-3.3.md b/UPGRADE-3.3.md index 53ff97fbe362b..4a3337d5798dd 100644 --- a/UPGRADE-3.3.md +++ b/UPGRADE-3.3.md @@ -127,6 +127,55 @@ Finder * The `ExceptionInterface` has been deprecated and will be removed in 4.0. +Form +---- + + * Using `choice_attr` option as an array of nested arrays has been deprecated + and indexes will be considered as attributes in 4.0. Use a unique array for + all choices or a `callable` instead. + + Before: + + ```php + // Single array for all choices using callable + 'choice_attr' => function () { + return array('class' => 'choice-options'); + }, + + // Different arrays per choices using array + 'choices' => array( + 'Yes' => true, + 'No' => false, + 'Maybe' => null, + 'choice_attr' => array( + 'Yes' => array('class' => 'option-green'), + 'No' => array('class' => 'option-red'), + ), + ``` + + After: + + ```php + // Single array for all choices using array + 'choice_attr' => array('class' => 'choice-options'), + + // Different arrays per choices using callable + 'choices' => array( + 'Yes' => true, + 'No' => false, + 'Maybe' => null, + 'choice_attr' => function ($choice, $index, $value) { + if ('Yes' === $index) { + return array('class' => 'option-green'); + } + if ('No' === $index) { + return array('class' => 'option-red'); + } + + return array(); + }, + ``` + FrameworkBundle --------------- diff --git a/UPGRADE-4.0.md b/UPGRADE-4.0.md index ba79eaa80f1e0..8ca8fd01c1e3e 100644 --- a/UPGRADE-4.0.md +++ b/UPGRADE-4.0.md @@ -187,6 +187,52 @@ Form } ``` + * Usage of `choice_attr` option as an array of nested arrays has been removed + and indexes will be considered as attributes in 4.0. Use a unique array for + all choices or a `callable` instead. + + Before: + + ```php + // Single array for all choices using callable + 'choice_attr' => function () { + return array('class' => 'choice-options'); + }, + + // Different arrays per choices using array + 'choices' => array( + 'Yes' => true, + 'No' => false, + 'Maybe' => null, + 'choice_attr' => array( + 'Yes' => array('class' => 'option-green'), + 'No' => array('class' => 'option-red'), + ), + ``` + + After: + + ```php + // Single array for all choices using array + 'choice_attr' => array('class' => 'choice-options'), + + // Different arrays per choices using callable + 'choices' => array( + 'Yes' => true, + 'No' => false, + 'Maybe' => null, + 'choice_attr' => function ($choice, $index, $value) { + if ('Yes' === $index) { + return array('class' => 'option-green'); + } + if ('No' === $index) { + return array('class' => 'option-red'); + } + + return array(); + }, + ``` + FrameworkBundle --------------- diff --git a/src/Symfony/Bridge/Twig/composer.json b/src/Symfony/Bridge/Twig/composer.json index 3f65bfaafa386..d829a59457f01 100644 --- a/src/Symfony/Bridge/Twig/composer.json +++ b/src/Symfony/Bridge/Twig/composer.json @@ -22,7 +22,7 @@ "require-dev": { "symfony/asset": "~2.8|~3.0", "symfony/finder": "~2.8|~3.0", - "symfony/form": "^3.2.5", + "symfony/form": "~3.3", "symfony/http-kernel": "~3.2", "symfony/polyfill-intl-icu": "~1.0", "symfony/routing": "~2.8|~3.0", diff --git a/src/Symfony/Component/Form/CHANGELOG.md b/src/Symfony/Component/Form/CHANGELOG.md index 1123119356af1..7d8a214f916f9 100644 --- a/src/Symfony/Component/Form/CHANGELOG.md +++ b/src/Symfony/Component/Form/CHANGELOG.md @@ -7,6 +7,7 @@ CHANGELOG * added `Symfony\Component\Form\FormErrorIterator::findByCodes()` * added `getTypedExtensions`, `getTypes`, and `getTypeGuessers` to `Symfony\Component\Form\Test\FormIntegrationTestCase` * added `FormPass` + * deprecated `choice_attr` option as array of nested arrays mapped by indexes 3.2.0 ----- diff --git a/src/Symfony/Component/Form/ChoiceList/Factory/DefaultChoiceListFactory.php b/src/Symfony/Component/Form/ChoiceList/Factory/DefaultChoiceListFactory.php index 3398c98edd742..fdb5afabce90b 100644 --- a/src/Symfony/Component/Form/ChoiceList/Factory/DefaultChoiceListFactory.php +++ b/src/Symfony/Component/Form/ChoiceList/Factory/DefaultChoiceListFactory.php @@ -131,13 +131,31 @@ private static function addChoiceView($choice, $value, $label, $keys, &$index, $ $label = false === $dynamicLabel ? false : (string) $dynamicLabel; } + // BC + if (is_array($attr)) { + if (isset($attr[$key])) { + @trigger_error('Passing an array of arrays to the "choice_attr" option with choice keys as keys is deprecated since version 3.3 and will no longer be supported in 4.0. Use a "\Closure" instead.', E_USER_DEPRECATED); + $attr = $attr[$key]; + } else { + foreach ($attr as $a) { + if (is_array($a)) { + // Using the deprecated way of choice keys as keys allows to not define all choices. + // When $attr[$key] is not set for this one but is for another we need to + // prevent using an array as HTML attribute + $attr = array(); + + break; + } + } + } + } + $view = new ChoiceView( $choice, $value, $label, - // The attributes may be a callable or a mapping from choice indices - // to nested arrays - is_callable($attr) ? call_user_func($attr, $choice, $key, $value) : (isset($attr[$key]) ? $attr[$key] : array()) + // The attributes may be a callable or an array + is_callable($attr) ? call_user_func($attr, $choice, $key, $value) : (null !== $attr ? $attr : array()) ); // $isPreferred may be null if no choices are preferred diff --git a/src/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php b/src/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php index b117f70edf265..d0e882b02c63c 100644 --- a/src/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php +++ b/src/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php @@ -329,7 +329,7 @@ public function configureOptions(OptionsResolver $resolver) 'choice_label' => null, 'choice_name' => null, 'choice_value' => null, - 'choice_attr' => null, + 'choice_attr' => array(), 'preferred_choices' => array(), 'group_by' => null, 'empty_data' => $emptyData, diff --git a/src/Symfony/Component/Form/Tests/AbstractBootstrap3LayoutTest.php b/src/Symfony/Component/Form/Tests/AbstractBootstrap3LayoutTest.php index 09138b1da7dcd..6c50833cdd44d 100644 --- a/src/Symfony/Component/Form/Tests/AbstractBootstrap3LayoutTest.php +++ b/src/Symfony/Component/Form/Tests/AbstractBootstrap3LayoutTest.php @@ -359,7 +359,10 @@ public function testSingleChoiceWithPlaceholderWithoutTranslation() ); } - public function testSingleChoiceAttributes() + /** + * @group legacy + */ + public function testLegacySingleChoiceAttributes() { $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', '&a', array( 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'), @@ -382,6 +385,54 @@ public function testSingleChoiceAttributes() ); } + public function testSingleChoiceAttributes() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', '&a', array( + 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'), + 'choice_attr' => array('class' => 'foo&bar'), + 'multiple' => false, + 'expanded' => false, + )); + + $this->assertWidgetMatchesXpath($form->createView(), array('attr' => array('class' => 'my&class')), +'/select + [@name="name"] + [@class="my&class form-control"] + [not(@required)] + [ + ./option[@value="&a"][@class="foo&bar"][@selected="selected"][.="[trans]Choice&A[/trans]"] + /following-sibling::option[@value="&b"][@class="foo&bar"][not(@selected)][.="[trans]Choice&B[/trans]"] + ] + [count(./option)=2] +' + ); + } + + public function testSingleChoiceAttributesSetByCallable() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', '&a', array( + 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'), + 'choice_attr' => function ($choice, $key, $value) { + return '&b' === $choice ? array('class' => 'foo&bar') : array(); + }, + 'multiple' => false, + 'expanded' => false, + )); + + $this->assertWidgetMatchesXpath($form->createView(), array('attr' => array('class' => 'my&class')), +'/select + [@name="name"] + [@class="my&class form-control"] + [not(@required)] + [ + ./option[@value="&a"][@selected="selected"][.="[trans]Choice&A[/trans]"] + /following-sibling::option[@value="&b"][@class="foo&bar"][not(@selected)][.="[trans]Choice&B[/trans]"] + ] + [count(./option)=2] +' + ); + } + public function testSingleChoiceWithPreferred() { $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', '&a', array( @@ -647,7 +698,10 @@ public function testMultipleChoice() ); } - public function testMultipleChoiceAttributes() + /** + * @group legacy + */ + public function testLegacyMultipleChoiceAttributes() { $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', array('&a'), array( 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'), @@ -672,6 +726,58 @@ public function testMultipleChoiceAttributes() ); } + public function testMultipleChoiceAttributes() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', array('&a'), array( + 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'), + 'choice_attr' => array('class' => 'foo&bar'), + 'required' => true, + 'multiple' => true, + 'expanded' => false, + )); + + $this->assertWidgetMatchesXpath($form->createView(), array('attr' => array('class' => 'my&class')), +'/select + [@name="name[]"] + [@class="my&class form-control"] + [@required="required"] + [@multiple="multiple"] + [ + ./option[@value="&a"][@class="foo&bar"][@selected="selected"][.="[trans]Choice&A[/trans]"] + /following-sibling::option[@value="&b"][@class="foo&bar"][not(@selected)][.="[trans]Choice&B[/trans]"] + ] + [count(./option)=2] +' + ); + } + + public function testMultipleChoiceAttributesSetByCallable() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', array('&a'), array( + 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'), + 'choice_attr' => function ($choice, $key, $value) { + return '&b' === $choice ? array('class' => 'foo&bar') : array(); + }, + 'required' => true, + 'multiple' => true, + 'expanded' => false, + )); + + $this->assertWidgetMatchesXpath($form->createView(), array('attr' => array('class' => 'my&class')), +'/select + [@name="name[]"] + [@class="my&class form-control"] + [@required="required"] + [@multiple="multiple"] + [ + ./option[@value="&a"][@selected="selected"][.="[trans]Choice&A[/trans]"] + /following-sibling::option[@value="&b"][@class="foo&bar"][not(@selected)][.="[trans]Choice&B[/trans]"] + ] + [count(./option)=2] +' + ); + } + public function testMultipleChoiceSkipsPlaceholder() { $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', array('&a'), array( @@ -909,7 +1015,10 @@ public function testSingleChoiceExpandedWithoutTranslation() ); } - public function testSingleChoiceExpandedAttributes() + /** + * @group legacy + */ + public function testLegacySingleChoiceExpandedAttributes() { $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', '&a', array( 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'), @@ -945,6 +1054,80 @@ public function testSingleChoiceExpandedAttributes() ); } + public function testSingleChoiceExpandedAttributes() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', '&a', array( + 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'), + 'choice_attr' => array('class' => 'foo&bar'), + 'multiple' => false, + 'expanded' => true, + )); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/div + [ + ./div + [@class="radio"] + [ + ./label + [.=" [trans]Choice&A[/trans]"] + [ + ./input[@type="radio"][@name="name"][@id="name_0"][@value="&a"][@checked][@class="foo&bar"] + ] + ] + /following-sibling::div + [@class="radio"] + [ + ./label + [.=" [trans]Choice&B[/trans]"] + [ + ./input[@type="radio"][@name="name"][@id="name_1"][@value="&b"][not(@checked)][@class="foo&bar"] + ] + ] + /following-sibling::input[@type="hidden"][@id="name__token"] + ] +' + ); + } + + public function testSingleChoiceExpandedAttributesSetByCallable() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', '&a', array( + 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'), + 'choice_attr' => function ($choice, $key, $value) { + return '&b' == $choice ? array('class' => 'foo&bar') : array(); + }, + 'multiple' => false, + 'expanded' => true, + )); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/div + [ + ./div + [@class="radio"] + [ + ./label + [.=" [trans]Choice&A[/trans]"] + [ + ./input[@type="radio"][@name="name"][@id="name_0"][@value="&a"][@checked] + ] + ] + /following-sibling::div + [@class="radio"] + [ + ./label + [.=" [trans]Choice&B[/trans]"] + [ + ./input[@type="radio"][@name="name"][@id="name_1"][@value="&b"][not(@checked)][@class="foo&bar"] + ] + ] + /following-sibling::input[@type="hidden"][@id="name__token"] + ] +' + ); + } + public function testSingleChoiceExpandedWithPlaceholder() { $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', '&a', array( @@ -1284,7 +1467,10 @@ public function testMultipleChoiceExpandedWithoutTranslation() ); } - public function testMultipleChoiceExpandedAttributes() + /** + * @group legacy + */ + public function testLegacyMultipleChoiceExpandedAttributes() { $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', array('&a', '&c'), array( 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b', 'Choice&C' => '&c'), @@ -1330,6 +1516,100 @@ public function testMultipleChoiceExpandedAttributes() ); } + public function testMultipleChoiceExpandedAttributes() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', array('&a', '&c'), array( + 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b', 'Choice&C' => '&c'), + 'choice_attr' => array('class' => 'foo&bar'), + 'multiple' => true, + 'expanded' => true, + 'required' => true, + )); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/div + [ + ./div + [@class="checkbox"] + [ + ./label + [.=" [trans]Choice&A[/trans]"] + [ + ./input[@type="checkbox"][@name="name[]"][@id="name_0"][@checked][not(@required)][@class="foo&bar"] + ] + ] + /following-sibling::div + [@class="checkbox"] + [ + ./label + [.=" [trans]Choice&B[/trans]"] + [ + ./input[@type="checkbox"][@name="name[]"][@id="name_1"][not(@checked)][not(@required)][@class="foo&bar"] + ] + ] + /following-sibling::div + [@class="checkbox"] + [ + ./label + [.=" [trans]Choice&C[/trans]"] + [ + ./input[@type="checkbox"][@name="name[]"][@id="name_2"][@checked][not(@required)][@class="foo&bar"] + ] + ] + /following-sibling::input[@type="hidden"][@id="name__token"] + ] +' + ); + } + + public function testMultipleChoiceExpandedAttributesSetByCallable() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', array('&a', '&c'), array( + 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b', 'Choice&C' => '&c'), + 'choice_attr' => function ($choice, $key, $value) { + return '&b' === $choice ? array('class' => 'foo&bar') : array(); + }, + 'multiple' => true, + 'expanded' => true, + 'required' => true, + )); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/div + [ + ./div + [@class="checkbox"] + [ + ./label + [.=" [trans]Choice&A[/trans]"] + [ + ./input[@type="checkbox"][@name="name[]"][@id="name_0"][@checked][not(@required)] + ] + ] + /following-sibling::div + [@class="checkbox"] + [ + ./label + [.=" [trans]Choice&B[/trans]"] + [ + ./input[@type="checkbox"][@name="name[]"][@id="name_1"][not(@checked)][not(@required)][@class="foo&bar"] + ] + ] + /following-sibling::div + [@class="checkbox"] + [ + ./label + [.=" [trans]Choice&C[/trans]"] + [ + ./input[@type="checkbox"][@name="name[]"][@id="name_2"][@checked][not(@required)] + ] + ] + /following-sibling::input[@type="hidden"][@id="name__token"] + ] +' + ); + } + public function testCountry() { $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\CountryType', 'AT'); diff --git a/src/Symfony/Component/Form/Tests/AbstractLayoutTest.php b/src/Symfony/Component/Form/Tests/AbstractLayoutTest.php index 55d1d74620f06..4b563ab8bfa36 100644 --- a/src/Symfony/Component/Form/Tests/AbstractLayoutTest.php +++ b/src/Symfony/Component/Form/Tests/AbstractLayoutTest.php @@ -586,7 +586,10 @@ public function testSingleChoiceWithPlaceholderWithoutTranslation() ); } - public function testSingleChoiceAttributes() + /** + * @group legacy + */ + public function testLegacySingleChoiceAttributes() { $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', '&a', array( 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'), @@ -655,6 +658,52 @@ public function testSingleExpandedChoiceAttributesWithMainAttributes() ); } + public function testSingleChoiceAttributes() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', '&a', array( + 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'), + 'choice_attr' => array('class' => 'foo&bar'), + 'multiple' => false, + 'expanded' => false, + )); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/select + [@name="name"] + [not(@required)] + [ + ./option[@value="&a"][@class="foo&bar"][@selected="selected"][.="[trans]Choice&A[/trans]"] + /following-sibling::option[@value="&b"][@class="foo&bar"][not(@selected)][.="[trans]Choice&B[/trans]"] + ] + [count(./option)=2] +' + ); + } + + public function testSingleChoiceAttributesSetByCallable() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', '&a', array( + 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'), + 'choice_attr' => function ($choice, $key, $value) { + return '&b' === $choice ? array('class' => 'foo&bar') : array(); + }, + 'multiple' => false, + 'expanded' => false, + )); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/select + [@name="name"] + [not(@required)] + [ + ./option[@value="&a"][@selected="selected"][.="[trans]Choice&A[/trans]"] + /following-sibling::option[@value="&b"][@class="foo&bar"][not(@selected)][.="[trans]Choice&B[/trans]"] + ] + [count(./option)=2] +' + ); + } + public function testSingleChoiceWithPreferred() { $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', '&a', array( @@ -915,7 +964,10 @@ public function testMultipleChoice() ); } - public function testMultipleChoiceAttributes() + /** + * @group legacy + */ + public function testLegacyMultipleChoiceAttributes() { $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', array('&a'), array( 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'), @@ -939,6 +991,56 @@ public function testMultipleChoiceAttributes() ); } + public function testMultipleChoiceAttributes() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', array('&a'), array( + 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'), + 'choice_attr' => array('class' => 'foo&bar'), + 'required' => true, + 'multiple' => true, + 'expanded' => false, + )); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/select + [@name="name[]"] + [@required="required"] + [@multiple="multiple"] + [ + ./option[@value="&a"][@class="foo&bar"][@selected="selected"][.="[trans]Choice&A[/trans]"] + /following-sibling::option[@value="&b"][@class="foo&bar"][not(@selected)][.="[trans]Choice&B[/trans]"] + ] + [count(./option)=2] +' + ); + } + + public function testMultipleChoiceAttributesSetByCallable() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', array('&a'), array( + 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'), + 'choice_attr' => function ($choice, $key, $value) { + return '&b' === $choice ? array('class' => 'foo&bar') : array(); + }, + 'required' => true, + 'multiple' => true, + 'expanded' => false, + )); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/select + [@name="name[]"] + [@required="required"] + [@multiple="multiple"] + [ + ./option[@value="&a"][@selected="selected"][.="[trans]Choice&A[/trans]"] + /following-sibling::option[@value="&b"][@class="foo&bar"][not(@selected)][.="[trans]Choice&B[/trans]"] + ] + [count(./option)=2] +' + ); + } + public function testMultipleChoiceSkipsPlaceholder() { $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', array('&a'), array( @@ -1029,7 +1131,10 @@ public function testSingleChoiceExpandedWithoutTranslation() ); } - public function testSingleChoiceExpandedAttributes() + /** + * @group legacy + */ + public function testLegacySingleChoiceExpandedAttributes() { $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', '&a', array( 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'), @@ -1052,6 +1157,54 @@ public function testSingleChoiceExpandedAttributes() ); } + public function testSingleChoiceExpandedAttributes() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', '&a', array( + 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'), + 'choice_attr' => array('class' => 'foo&bar'), + 'multiple' => false, + 'expanded' => true, + )); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/div + [ + ./input[@type="radio"][@name="name"][@id="name_0"][@value="&a"][@class="foo&bar"][@checked] + /following-sibling::label[@for="name_0"][.="[trans]Choice&A[/trans]"] + /following-sibling::input[@type="radio"][@name="name"][@id="name_1"][@value="&b"][@class="foo&bar"][not(@checked)] + /following-sibling::label[@for="name_1"][.="[trans]Choice&B[/trans]"] + /following-sibling::input[@type="hidden"][@id="name__token"] + ] + [count(./input)=3] +' + ); + } + + public function testSingleChoiceExpandedAttributesSetByCallable() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', '&a', array( + 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'), + 'choice_attr' => function ($choice, $key, $value) { + return '&b' === $choice ? array('class' => 'foo&bar') : array(); + }, + 'multiple' => false, + 'expanded' => true, + )); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/div + [ + ./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"][@class="foo&bar"][not(@checked)] + /following-sibling::label[@for="name_1"][.="[trans]Choice&B[/trans]"] + /following-sibling::input[@type="hidden"][@id="name__token"] + ] + [count(./input)=3] +' + ); + } + public function testSingleChoiceExpandedWithPlaceholder() { $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', '&a', array( @@ -1178,7 +1331,10 @@ public function testMultipleChoiceExpandedWithoutTranslation() ); } - public function testMultipleChoiceExpandedAttributes() + /** + * @group legacy + */ + public function testLegacyMultipleChoiceExpandedAttributes() { $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', array('&a', '&c'), array( 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b', 'Choice&C' => '&c'), @@ -1204,6 +1360,60 @@ public function testMultipleChoiceExpandedAttributes() ); } + public function testMultipleChoiceExpandedAttributes() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', array('&a', '&c'), array( + 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b', 'Choice&C' => '&c'), + 'choice_attr' => array('class' => 'foo&bar'), + 'multiple' => true, + 'expanded' => true, + 'required' => true, + )); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/div + [ + ./input[@type="checkbox"][@name="name[]"][@id="name_0"][@class="foo&bar"][@checked][not(@required)] + /following-sibling::label[@for="name_0"][.="[trans]Choice&A[/trans]"] + /following-sibling::input[@type="checkbox"][@name="name[]"][@id="name_1"][@class="foo&bar"][not(@checked)][not(@required)] + /following-sibling::label[@for="name_1"][.="[trans]Choice&B[/trans]"] + /following-sibling::input[@type="checkbox"][@name="name[]"][@id="name_2"][@class="foo&bar"][@checked][not(@required)] + /following-sibling::label[@for="name_2"][.="[trans]Choice&C[/trans]"] + /following-sibling::input[@type="hidden"][@id="name__token"] + ] + [count(./input)=4] +' + ); + } + + public function testMultipleChoiceExpandedAttributesSetByCallable() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', array('&a', '&c'), array( + 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b', 'Choice&C' => '&c'), + 'choice_attr' => function ($choice, $key, $value) { + return '&b' === $choice ? array('class' => 'foo&bar') : array(); + }, + 'multiple' => true, + 'expanded' => true, + 'required' => true, + )); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/div + [ + ./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"][@class="foo&bar"][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] +' + ); + } + public function testCountry() { $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\CountryType', 'AT'); diff --git a/src/Symfony/Component/Form/Tests/ChoiceList/Factory/DefaultChoiceListFactoryTest.php b/src/Symfony/Component/Form/Tests/ChoiceList/Factory/DefaultChoiceListFactoryTest.php index 2450248240833..2e3e87db3c6a1 100644 --- a/src/Symfony/Component/Form/Tests/ChoiceList/Factory/DefaultChoiceListFactoryTest.php +++ b/src/Symfony/Component/Form/Tests/ChoiceList/Factory/DefaultChoiceListFactoryTest.php @@ -523,7 +523,10 @@ function ($object, $key, $value) { $this->assertGroupedView($view); } - public function testCreateViewFlatAttrAsArray() + /** + * @group legacy + */ + public function testLegacyCreateViewFlatAttrAsArray() { $view = $this->factory->createView( $this->list, @@ -537,6 +540,20 @@ public function testCreateViewFlatAttrAsArray() ) ); + $this->assertFlatViewWithDynamicAttr($view); + } + + public function testCreateViewFlatAttrAsArray() + { + $view = $this->factory->createView( + $this->list, + array($this->obj2, $this->obj3), + null, // label + null, // index + null, // group + array('attr' => 'value') + ); + $this->assertFlatViewWithAttr($view); } @@ -565,7 +582,7 @@ public function testCreateViewFlatAttrAsCallable() array($this, 'getAttr') ); - $this->assertFlatViewWithAttr($view); + $this->assertFlatViewWithDynamicAttr($view); } public function testCreateViewFlatAttrAsClosure() @@ -581,7 +598,7 @@ function ($object) { } ); - $this->assertFlatViewWithAttr($view); + $this->assertFlatViewWithDynamicAttr($view); } public function testCreateViewFlatAttrClosureReceivesKey() @@ -601,7 +618,7 @@ function ($object, $key) { } ); - $this->assertFlatViewWithAttr($view); + $this->assertFlatViewWithDynamicAttr($view); } public function testCreateViewFlatAttrClosureReceivesValue() @@ -621,7 +638,7 @@ function ($object, $key, $value) { } ); - $this->assertFlatViewWithAttr($view); + $this->assertFlatViewWithDynamicAttr($view); } private function assertScalarListWithChoiceValues(ChoiceListInterface $list) @@ -726,7 +743,7 @@ private function assertFlatViewWithCustomIndices($view) ), $view); } - private function assertFlatViewWithAttr($view) + private function assertFlatViewWithDynamicAttr($view) { $this->assertEquals(new ChoiceListView( array( @@ -749,6 +766,39 @@ private function assertFlatViewWithAttr($view) ), $view); } + private function assertFlatViewWithAttr($view) + { + $this->assertEquals(new ChoiceListView( + array( + 0 => new ChoiceView( + $this->obj1, + '0', + 'A', + array('attr' => 'value') + ), + 3 => new ChoiceView( + $this->obj4, + '3', + 'D', + array('attr' => 'value') + ), + ), array( + 1 => new ChoiceView( + $this->obj2, + '1', + 'B', + array('attr' => 'value') + ), + 2 => new ChoiceView( + $this->obj3, + '2', + 'C', + array('attr' => 'value') + ), + ) + ), $view); + } + private function assertGroupedView($view) { $this->assertEquals(new ChoiceListView( From eb658f971ad7b9d2047530b896166d1f522ecf53 Mon Sep 17 00:00:00 2001 From: HeahDude Date: Sun, 25 Sep 2016 17:10:51 +0200 Subject: [PATCH 2/4] [Form] deprecated "choice_attr" option as PropertyPath --- UPGRADE-3.3.md | 16 ++++++++++++++++ UPGRADE-4.0.md | 17 +++++++++++++++++ src/Symfony/Component/Form/CHANGELOG.md | 1 + .../Factory/PropertyAccessDecorator.php | 1 + .../Form/Extension/Core/Type/ChoiceType.php | 10 ++++++++++ .../Factory/PropertyAccessDecoratorTest.php | 6 ++++++ 6 files changed, 51 insertions(+) diff --git a/UPGRADE-3.3.md b/UPGRADE-3.3.md index 4a3337d5798dd..d6db853f57bb9 100644 --- a/UPGRADE-3.3.md +++ b/UPGRADE-3.3.md @@ -176,6 +176,22 @@ Form }, ``` + * Using `choice_attr` option as a string or a `ProprertyPath` instance has been + deprecated and will throw an exception in 4.0. Use a `callable` instead. + + Before: + + ```php + 'choice_attr' => 'htmlAttributes', + ``` + + After: + + ```php + 'choice_attr' => function ($choice, $value, $index) { + return $choice->getHtmlAttributes(); + ``` + FrameworkBundle --------------- diff --git a/UPGRADE-4.0.md b/UPGRADE-4.0.md index 8ca8fd01c1e3e..bf4e87078056c 100644 --- a/UPGRADE-4.0.md +++ b/UPGRADE-4.0.md @@ -233,6 +233,23 @@ Form }, ``` + * Using `choice_attr` option as a string or a `ProprertyPath` instance will + throw an exception. Use a `callable` instead. + + Before: + + ```php + 'choice_attr' => 'htmlAttributes', + ``` + + After: + + ```php + 'choice_attr' => function ($choice, $value, $index) { + return $choice->getHtmlAttributes(); + }, + ``` + FrameworkBundle --------------- diff --git a/src/Symfony/Component/Form/CHANGELOG.md b/src/Symfony/Component/Form/CHANGELOG.md index 7d8a214f916f9..eaf0485f47963 100644 --- a/src/Symfony/Component/Form/CHANGELOG.md +++ b/src/Symfony/Component/Form/CHANGELOG.md @@ -8,6 +8,7 @@ CHANGELOG * added `getTypedExtensions`, `getTypes`, and `getTypeGuessers` to `Symfony\Component\Form\Test\FormIntegrationTestCase` * added `FormPass` * deprecated `choice_attr` option as array of nested arrays mapped by indexes + * deprecated `choice_attr` option as string or `PropertyPath` instance 3.2.0 ----- diff --git a/src/Symfony/Component/Form/ChoiceList/Factory/PropertyAccessDecorator.php b/src/Symfony/Component/Form/ChoiceList/Factory/PropertyAccessDecorator.php index 0e282f7083da5..d348435c2a42c 100644 --- a/src/Symfony/Component/Form/ChoiceList/Factory/PropertyAccessDecorator.php +++ b/src/Symfony/Component/Form/ChoiceList/Factory/PropertyAccessDecorator.php @@ -218,6 +218,7 @@ public function createView(ChoiceListInterface $list, $preferredChoices = null, @trigger_error('Passing callable strings is deprecated since version 3.1 and PropertyAccessDecorator will treat them as property paths in 4.0. You should use a "\Closure" instead.', E_USER_DEPRECATED); } + // Deprecated since 3.3 and to be removed in 4.0 with the condition above if ($attr instanceof PropertyPath) { $attr = function ($choice) use ($accessor, $attr) { return $accessor->getValue($choice, $attr); diff --git a/src/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php b/src/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php index d0e882b02c63c..5a9a23dcec260 100644 --- a/src/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php +++ b/src/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php @@ -34,6 +34,7 @@ use Symfony\Component\Form\Util\FormUtil; use Symfony\Component\OptionsResolver\Options; use Symfony\Component\OptionsResolver\OptionsResolver; +use Symfony\Component\PropertyAccess\PropertyPath; class ChoiceType extends AbstractType { @@ -320,6 +321,14 @@ public function configureOptions(OptionsResolver $resolver) return $choiceTranslationDomain; }; + $choiceAttrNormalizer = function (Options $options, $choiceAttr) { + if (is_string($choiceAttr) || $choiceAttr instanceof PropertyPath) { + @trigger_error(sprintf('Using "choice_attr" option as a string property path or a "%s" instance is deprecated since version 3.3 and will throw an exception in 4.0. Use a callable instead.', PropertyPath::class), E_USER_DEPRECATED); + } + + return $choiceAttr; + }; + $resolver->setDefaults(array( 'multiple' => false, 'expanded' => false, @@ -346,6 +355,7 @@ public function configureOptions(OptionsResolver $resolver) $resolver->setNormalizer('placeholder', $placeholderNormalizer); $resolver->setNormalizer('choice_translation_domain', $choiceTranslationDomainNormalizer); $resolver->setNormalizer('choices_as_values', $choicesAsValuesNormalizer); + $resolver->setNormalizer('choice_attr', $choiceAttrNormalizer); $resolver->setAllowedTypes('choices', array('null', 'array', '\Traversable')); $resolver->setAllowedTypes('choice_translation_domain', array('null', 'bool', 'string')); diff --git a/src/Symfony/Component/Form/Tests/ChoiceList/Factory/PropertyAccessDecoratorTest.php b/src/Symfony/Component/Form/Tests/ChoiceList/Factory/PropertyAccessDecoratorTest.php index d100dcff4a5fd..a837ce5e3b737 100644 --- a/src/Symfony/Component/Form/Tests/ChoiceList/Factory/PropertyAccessDecoratorTest.php +++ b/src/Symfony/Component/Form/Tests/ChoiceList/Factory/PropertyAccessDecoratorTest.php @@ -417,6 +417,9 @@ public function testCreateViewAssumeNullIfGroupsPropertyPathUnreadable() )); } + /** + * @group legacy + */ public function testCreateViewAttrAsPropertyPath() { $list = $this->getMockBuilder('Symfony\Component\Form\ChoiceList\ChoiceListInterface')->getMock(); @@ -460,6 +463,9 @@ public function testCreateViewAttrAsPropertyPathWithCallableString() )); } + /** + * @group legacy + */ public function testCreateViewAttrAsPropertyPathInstance() { $list = $this->getMockBuilder('Symfony\Component\Form\ChoiceList\ChoiceListInterface')->getMock(); From 23df93ddb70802f35b958ea0d04efdf4d59e54e9 Mon Sep 17 00:00:00 2001 From: Jules Pietri Date: Fri, 25 Mar 2016 19:26:33 +0100 Subject: [PATCH 3/4] [Form] add `choice_label_attr` option to `ChoiceType` --- ...xtensionBootstrap3HorizontalLayoutTest.php | 1 + src/Symfony/Component/Form/CHANGELOG.md | 1 + .../Factory/CachingFactoryDecorator.php | 40 +++- .../Factory/DefaultChoiceListFactory.php | 25 ++- .../ExpandedChoiceListFactoryInterface.php | 76 ++++++++ .../Factory/PropertyAccessDecorator.php | 19 +- .../Form/ChoiceList/View/ChoiceView.php | 19 +- .../Form/Extension/Core/Type/ChoiceType.php | 16 ++ .../Tests/AbstractBootstrap3LayoutTest.php | 175 ++++++++++++++++++ .../Form/Tests/AbstractLayoutTest.php | 102 ++++++++++ 10 files changed, 452 insertions(+), 22 deletions(-) create mode 100644 src/Symfony/Component/Form/ChoiceList/Factory/ExpandedChoiceListFactoryInterface.php diff --git a/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionBootstrap3HorizontalLayoutTest.php b/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionBootstrap3HorizontalLayoutTest.php index 614b59f5823ec..93ebc1b841983 100644 --- a/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionBootstrap3HorizontalLayoutTest.php +++ b/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionBootstrap3HorizontalLayoutTest.php @@ -26,6 +26,7 @@ class FormExtensionBootstrap3HorizontalLayoutTest extends AbstractBootstrap3Hori protected $testableFeatures = array( 'choice_attr', + 'choice_label_attr', ); private $renderer; diff --git a/src/Symfony/Component/Form/CHANGELOG.md b/src/Symfony/Component/Form/CHANGELOG.md index eaf0485f47963..e440a50c066de 100644 --- a/src/Symfony/Component/Form/CHANGELOG.md +++ b/src/Symfony/Component/Form/CHANGELOG.md @@ -9,6 +9,7 @@ CHANGELOG * added `FormPass` * deprecated `choice_attr` option as array of nested arrays mapped by indexes * deprecated `choice_attr` option as string or `PropertyPath` instance + * added `choice_label_attr` option to `ChoiceType` to use when expanded 3.2.0 ----- diff --git a/src/Symfony/Component/Form/ChoiceList/Factory/CachingFactoryDecorator.php b/src/Symfony/Component/Form/ChoiceList/Factory/CachingFactoryDecorator.php index 6580e661d4d66..ebed4728b89d0 100644 --- a/src/Symfony/Component/Form/ChoiceList/Factory/CachingFactoryDecorator.php +++ b/src/Symfony/Component/Form/ChoiceList/Factory/CachingFactoryDecorator.php @@ -20,7 +20,7 @@ * * @author Bernhard Schussek */ -class CachingFactoryDecorator implements ChoiceListFactoryInterface +class CachingFactoryDecorator implements ExpandedChoiceListFactoryInterface { /** * @var ChoiceListFactoryInterface @@ -153,20 +153,42 @@ public function createListFromLoader(ChoiceLoaderInterface $loader, $value = nul * {@inheritdoc} */ public function createView(ChoiceListInterface $list, $preferredChoices = null, $label = null, $index = null, $groupBy = null, $attr = null) + { + return $this->createExpandedView($list, $preferredChoices, $label, $index, $groupBy, $attr); + } + + /** + * {@inheritdoc} + */ + public function createExpandedView(ChoiceListInterface $list, $preferredChoices = null, $label = null, $index = null, $groupBy = null, $attr = null, $labelAttr = null) { // The input is not validated on purpose. This way, the decorated // factory may decide which input to accept and which not. $hash = self::generateHash(array($list, $preferredChoices, $label, $index, $groupBy, $attr)); if (!isset($this->views[$hash])) { - $this->views[$hash] = $this->decoratedFactory->createView( - $list, - $preferredChoices, - $label, - $index, - $groupBy, - $attr - ); + // BC layer + if ($this->decoratedFactory instanceof ExpandedChoiceListFactoryInterface) { + $choiceView = $this->decoratedFactory->createExpandedView( + $list, + $preferredChoices, + $label, + $index, + $groupBy, + $attr, + $labelAttr + ); + } else { + $choiceView = $this->decoratedFactory->createView( + $list, + $preferredChoices, + $label, + $index, + $groupBy, + $attr + ); + } + $this->views[$hash] = $choiceView; } return $this->views[$hash]; diff --git a/src/Symfony/Component/Form/ChoiceList/Factory/DefaultChoiceListFactory.php b/src/Symfony/Component/Form/ChoiceList/Factory/DefaultChoiceListFactory.php index fdb5afabce90b..e5d9cc49f1df7 100644 --- a/src/Symfony/Component/Form/ChoiceList/Factory/DefaultChoiceListFactory.php +++ b/src/Symfony/Component/Form/ChoiceList/Factory/DefaultChoiceListFactory.php @@ -24,7 +24,7 @@ * * @author Bernhard Schussek */ -class DefaultChoiceListFactory implements ChoiceListFactoryInterface +class DefaultChoiceListFactory implements ExpandedChoiceListFactoryInterface { /** * {@inheritdoc} @@ -46,6 +46,14 @@ public function createListFromLoader(ChoiceLoaderInterface $loader, $value = nul * {@inheritdoc} */ public function createView(ChoiceListInterface $list, $preferredChoices = null, $label = null, $index = null, $groupBy = null, $attr = null) + { + return $this->createExpandedView($list, $preferredChoices, $label, $index, $groupBy, $attr); + } + + /** + * {@inheritdoc} + */ + public function createExpandedView(ChoiceListInterface $list, $preferredChoices = null, $label = null, $index = null, $groupBy = null, $attr = null, $labelAttr = null) { $preferredViews = array(); $otherViews = array(); @@ -76,6 +84,7 @@ public function createView(ChoiceListInterface $list, $preferredChoices = null, $keys, $index, $attr, + $labelAttr, $preferredChoices, $preferredViews, $otherViews @@ -90,6 +99,7 @@ public function createView(ChoiceListInterface $list, $preferredChoices = null, $keys, $index, $attr, + $labelAttr, $preferredChoices, $preferredViews, $otherViews @@ -113,7 +123,7 @@ public function createView(ChoiceListInterface $list, $preferredChoices = null, return new ChoiceListView($otherViews, $preferredViews); } - private static function addChoiceView($choice, $value, $label, $keys, &$index, $attr, $isPreferred, &$preferredViews, &$otherViews) + private static function addChoiceView($choice, $value, $label, $keys, &$index, $attr, $labelAttr, $isPreferred, &$preferredViews, &$otherViews) { // $value may be an integer or a string, since it's stored in the array // keys. We want to guarantee it's a string though. @@ -155,7 +165,8 @@ private static function addChoiceView($choice, $value, $label, $keys, &$index, $ $value, $label, // The attributes may be a callable or an array - is_callable($attr) ? call_user_func($attr, $choice, $key, $value) : (null !== $attr ? $attr : array()) + is_callable($attr) ? call_user_func($attr, $choice, $key, $value) : (null !== $attr ? $attr : array()), + is_callable($labelAttr) ? call_user_func($labelAttr, $choice, $key, $value) : (null !== $labelAttr ? $labelAttr : array()) ); // $isPreferred may be null if no choices are preferred @@ -166,7 +177,7 @@ private static function addChoiceView($choice, $value, $label, $keys, &$index, $ } } - private static function addChoiceViewsGroupedBy($groupBy, $label, $choices, $keys, &$index, $attr, $isPreferred, &$preferredViews, &$otherViews) + private static function addChoiceViewsGroupedBy($groupBy, $label, $choices, $keys, &$index, $attr, $labelAttr, $isPreferred, &$preferredViews, &$otherViews) { foreach ($groupBy as $key => $value) { if (null === $value) { @@ -185,6 +196,7 @@ private static function addChoiceViewsGroupedBy($groupBy, $label, $choices, $key $keys, $index, $attr, + $labelAttr, $isPreferred, $preferredViewsForGroup, $otherViewsForGroup @@ -209,6 +221,7 @@ private static function addChoiceViewsGroupedBy($groupBy, $label, $choices, $key $keys, $index, $attr, + $labelAttr, $isPreferred, $preferredViews, $otherViews @@ -216,7 +229,7 @@ private static function addChoiceViewsGroupedBy($groupBy, $label, $choices, $key } } - private static function addChoiceViewGroupedBy($groupBy, $choice, $value, $label, $keys, &$index, $attr, $isPreferred, &$preferredViews, &$otherViews) + private static function addChoiceViewGroupedBy($groupBy, $choice, $value, $label, $keys, &$index, $attr, $labelAttr, $isPreferred, &$preferredViews, &$otherViews) { $groupLabel = call_user_func($groupBy, $choice, $keys[$value], $value); @@ -229,6 +242,7 @@ private static function addChoiceViewGroupedBy($groupBy, $choice, $value, $label $keys, $index, $attr, + $labelAttr, $isPreferred, $preferredViews, $otherViews @@ -253,6 +267,7 @@ private static function addChoiceViewGroupedBy($groupBy, $choice, $value, $label $keys, $index, $attr, + $labelAttr, $isPreferred, $preferredViews[$groupLabel]->choices, $otherViews[$groupLabel]->choices diff --git a/src/Symfony/Component/Form/ChoiceList/Factory/ExpandedChoiceListFactoryInterface.php b/src/Symfony/Component/Form/ChoiceList/Factory/ExpandedChoiceListFactoryInterface.php new file mode 100644 index 0000000000000..24d9299bb3283 --- /dev/null +++ b/src/Symfony/Component/Form/ChoiceList/Factory/ExpandedChoiceListFactoryInterface.php @@ -0,0 +1,76 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\ChoiceList\Factory; + +use Symfony\Component\Form\ChoiceList\ChoiceListInterface; +use Symfony\Component\Form\ChoiceList\View\ChoiceListView; + +/** + * Creates {@link ChoiceListInterface} instances. + * + * Provides a BC layer for 3.x. + * + * To be deprecated in 3.4 and removed in 4.0, in favor of {@link ChoiceListFactoryInterface::createview()} + * + * @author Bernhard Schussek + * @author Jules Pietri + */ +interface ExpandedChoiceListFactoryInterface extends ChoiceListFactoryInterface +{ + /** + * Creates a view for the given choice list. + * + * Callables may be passed for all optional arguments. The callables receive + * the choice as first and the array key as the second argument. + * + * * The callable for the label and the name should return the generated + * label/choice name. + * * The callable for the preferred choices should return true or false, + * depending on whether the choice should be preferred or not. + * * The callable for the grouping should return the group name or null if + * a choice should not be grouped. + * * The callable for the attributes should return an array of HTML + * attributes that will be inserted in the tag of the choice. + * * The callable for the label attributes should return an array of HTML + * attributes that will be inserted in the tag of the choice. + * + * If no callable is passed, the labels will be generated from the choice + * keys. The view indices will be generated using an incrementing integer + * by default. + * + * The preferred choices can also be passed as array. Each choice that is + * contained in that array will be marked as preferred. + * + * The attributes can be passed as multi-dimensional array. The keys should + * match the keys of the choices. The values should be arrays of HTML + * attributes that should be added to the respective choice. + * + * The label attributes can be passed as an array. It will be used for + * each choice. + * + * @param ChoiceListInterface $list The choice list + * @param null|array|callable $preferredChoices The preferred choices + * @param null|callable $label The callable generating the + * choice labels + * @param null|callable $index The callable generating the + * view indices + * @param null|callable $groupBy The callable generating the + * group names + * @param null|array|callable $attr The callable generating the + * HTML attributes + * @param null|array|callable $labelAttr The callable generating the + * HTML attributes + * + * @return ChoiceListView The choice list view + */ + public function createExpandedView(ChoiceListInterface $list, $preferredChoices = null, $label = null, $index = null, $groupBy = null, $attr = null, $labelAttr = null); +} diff --git a/src/Symfony/Component/Form/ChoiceList/Factory/PropertyAccessDecorator.php b/src/Symfony/Component/Form/ChoiceList/Factory/PropertyAccessDecorator.php index d348435c2a42c..0e5faf994953f 100644 --- a/src/Symfony/Component/Form/ChoiceList/Factory/PropertyAccessDecorator.php +++ b/src/Symfony/Component/Form/ChoiceList/Factory/PropertyAccessDecorator.php @@ -39,7 +39,7 @@ * * @author Bernhard Schussek */ -class PropertyAccessDecorator implements ChoiceListFactoryInterface +class PropertyAccessDecorator implements ExpandedChoiceListFactoryInterface { /** * @var ChoiceListFactoryInterface @@ -139,6 +139,11 @@ public function createListFromLoader(ChoiceLoaderInterface $loader, $value = nul return $this->decoratedFactory->createListFromLoader($loader, $value); } + public function createView(ChoiceListInterface $list, $preferredChoices = null, $label = null, $index = null, $groupBy = null, $attr = null) + { + return $this->createExpandedView($list, $preferredChoices, $label, $index, $groupBy, $attr); + } + /** * {@inheritdoc} * @@ -148,10 +153,11 @@ public function createListFromLoader(ChoiceLoaderInterface $loader, $value = nul * @param null|callable|string|PropertyPath $index The callable or path generating the view indices * @param null|callable|string|PropertyPath $groupBy The callable or path generating the group names * @param null|array|callable|string|PropertyPath $attr The callable or path generating the HTML attributes + * @param null|array|callable $labelAttr The array or callable generating the label HTML attributes * * @return ChoiceListView The choice list view */ - public function createView(ChoiceListInterface $list, $preferredChoices = null, $label = null, $index = null, $groupBy = null, $attr = null) + public function createExpandedView(ChoiceListInterface $list, $preferredChoices = null, $label = null, $index = null, $groupBy = null, $attr = null, $labelAttr = null) { $accessor = $this->propertyAccessor; @@ -225,6 +231,13 @@ public function createView(ChoiceListInterface $list, $preferredChoices = null, }; } - return $this->decoratedFactory->createView($list, $preferredChoices, $label, $index, $groupBy, $attr); + // BC Layer + if ($this->decoratedFactory instanceof ExpandedChoiceListFactoryInterface) { + $choiceView = $this->decoratedFactory->createExpandedView($list, $preferredChoices, $label, $index, $groupBy, $attr, $labelAttr); + } else { + $choiceView = $this->decoratedFactory->createView($list, $preferredChoices, $label, $index, $groupBy, $attr); + } + + return $choiceView; } } diff --git a/src/Symfony/Component/Form/ChoiceList/View/ChoiceView.php b/src/Symfony/Component/Form/ChoiceList/View/ChoiceView.php index 6009597c044d8..201da8d12e2fd 100644 --- a/src/Symfony/Component/Form/ChoiceList/View/ChoiceView.php +++ b/src/Symfony/Component/Form/ChoiceList/View/ChoiceView.php @@ -46,19 +46,28 @@ class ChoiceView */ public $attr; + /** + * Additional label attributes for the HTML tag. + * + * @var array + */ + public $labelAttr; + /** * Creates a new choice view. * - * @param mixed $data The original choice - * @param string $value The view representation of the choice - * @param string $label The label displayed to humans - * @param array $attr Additional attributes for the HTML tag + * @param mixed $data The original choice + * @param string $value The view representation of the choice + * @param string $label The label displayed to humans + * @param array $attr Additional attributes for the HTML tag + * @param array $labelAttr Additional label attributes for the HTML tag */ - public function __construct($data, $value, $label, array $attr = array()) + public function __construct($data, $value, $label, array $attr = array(), array $labelAttr = array()) { $this->data = $data; $this->value = $value; $this->label = $label; $this->attr = $attr; + $this->labelAttr = $labelAttr; } } diff --git a/src/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php b/src/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php index 5a9a23dcec260..f9ce0123450bc 100644 --- a/src/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php +++ b/src/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php @@ -13,6 +13,7 @@ use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\ChoiceList\Factory\CachingFactoryDecorator; +use Symfony\Component\Form\ChoiceList\Factory\ExpandedChoiceListFactoryInterface; use Symfony\Component\Form\ChoiceList\Factory\PropertyAccessDecorator; use Symfony\Component\Form\ChoiceList\View\ChoiceGroupView; use Symfony\Component\Form\ChoiceList\ChoiceListInterface; @@ -339,6 +340,7 @@ public function configureOptions(OptionsResolver $resolver) 'choice_name' => null, 'choice_value' => null, 'choice_attr' => array(), + 'choice_label_attr' => array(), 'preferred_choices' => array(), 'group_by' => null, 'empty_data' => $emptyData, @@ -364,6 +366,7 @@ public function configureOptions(OptionsResolver $resolver) $resolver->setAllowedTypes('choice_name', array('null', 'callable', 'string', 'Symfony\Component\PropertyAccess\PropertyPath')); $resolver->setAllowedTypes('choice_value', array('null', 'callable', 'string', 'Symfony\Component\PropertyAccess\PropertyPath')); $resolver->setAllowedTypes('choice_attr', array('null', 'array', 'callable', 'string', 'Symfony\Component\PropertyAccess\PropertyPath')); + $resolver->setAllowedTypes('choice_label_attr', array('array', 'callable')); $resolver->setAllowedTypes('preferred_choices', array('array', '\Traversable', 'callable', 'string', 'Symfony\Component\PropertyAccess\PropertyPath')); $resolver->setAllowedTypes('group_by', array('null', 'callable', 'string', 'Symfony\Component\PropertyAccess\PropertyPath')); } @@ -415,6 +418,7 @@ private function addSubForm(FormBuilderInterface $builder, $name, ChoiceView $ch 'value' => $choiceView->value, 'label' => $choiceView->label, 'attr' => $choiceView->attr, + 'label_attr' => $choiceView->labelAttr, 'translation_domain' => $options['translation_domain'], 'block_name' => 'entry', ); @@ -448,6 +452,18 @@ private function createChoiceList(array $options) private function createChoiceListView(ChoiceListInterface $choiceList, array $options) { + if ($this->choiceListFactory instanceof ExpandedChoiceListFactoryInterface) { + return $this->choiceListFactory->createExpandedView( + $choiceList, + $options['preferred_choices'], + $options['choice_label'], + $options['choice_name'], + $options['group_by'], + $options['choice_attr'], + $options['choice_label_attr'] + ); + } + return $this->choiceListFactory->createView( $choiceList, $options['preferred_choices'], diff --git a/src/Symfony/Component/Form/Tests/AbstractBootstrap3LayoutTest.php b/src/Symfony/Component/Form/Tests/AbstractBootstrap3LayoutTest.php index 6c50833cdd44d..e1993d9cbb765 100644 --- a/src/Symfony/Component/Form/Tests/AbstractBootstrap3LayoutTest.php +++ b/src/Symfony/Component/Form/Tests/AbstractBootstrap3LayoutTest.php @@ -1128,6 +1128,83 @@ public function testSingleChoiceExpandedAttributesSetByCallable() ); } + public function testSingleChoiceExpandedLabelAttributes() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', '&a', array( + 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'), + 'choice_label_attr' => array('class' => 'foo&bar'), + 'multiple' => false, + 'expanded' => true, + )); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/div + [ + ./div + [@class="radio"] + [ + ./label + [@class="foo&bar required"] + [.=" [trans]Choice&A[/trans]"] + [ + ./input[@type="radio"][@name="name"][@id="name_0"][@value="&a"][@checked] + ] + ] + /following-sibling::div + [@class="radio"] + [ + ./label + [@class="foo&bar required"] + [.=" [trans]Choice&B[/trans]"] + [ + ./input[@type="radio"][@name="name"][@id="name_1"][@value="&b"][not(@checked)] + ] + ] + /following-sibling::input[@type="hidden"][@id="name__token"] + ] +' + ); + } + + public function testSingleChoiceExpandedLabelAttributesSetByCallable() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', '&a', array( + 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'), + 'choice_label_attr' => function ($choice, $key, $value) { + return '&b' === $choice ? array('class' => 'foo&bar') : array(); + }, + 'multiple' => false, + 'expanded' => true, + )); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/div + [ + ./div + [@class="radio"] + [ + ./label + [.=" [trans]Choice&A[/trans]"] + [ + ./input[@type="radio"][@name="name"][@id="name_0"][@value="&a"][@checked] + ] + ] + /following-sibling::div + [@class="radio"] + [ + ./label + [@class="foo&bar required"] + [.=" [trans]Choice&B[/trans]"] + [ + ./input[@type="radio"][@name="name"][@id="name_1"][@value="&b"][not(@checked)] + ] + ] + /following-sibling::input[@type="hidden"][@id="name__token"] + ] +' + ); + } + public function testSingleChoiceExpandedWithPlaceholder() { $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', '&a', array( @@ -1610,6 +1687,104 @@ public function testMultipleChoiceExpandedAttributesSetByCallable() ); } + public function testMultipleChoiceExpandedLabelAttributes() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', array('&a', '&c'), array( + 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b', 'Choice&C' => '&c'), + 'choice_label_attr' => array('class' => 'foo&bar'), + 'multiple' => true, + 'expanded' => true, + 'required' => true, + )); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/div + [ + ./div + [@class="checkbox"] + [ + ./label + [@class="foo&bar"] + [.=" [trans]Choice&A[/trans]"] + [ + ./input[@type="checkbox"][@name="name[]"][@id="name_0"][@checked][not(@required)] + ] + ] + /following-sibling::div + [@class="checkbox"] + [ + ./label + [@class="foo&bar"] + [.=" [trans]Choice&B[/trans]"] + [ + ./input[@type="checkbox"][@name="name[]"][@id="name_1"][not(@checked)][not(@required)] + ] + ] + /following-sibling::div + [@class="checkbox"] + [ + ./label + [@class="foo&bar"] + [.=" [trans]Choice&C[/trans]"] + [ + ./input[@type="checkbox"][@name="name[]"][@id="name_2"][@checked][not(@required)] + ] + ] + /following-sibling::input[@type="hidden"][@id="name__token"] + ] +' + ); + } + + public function testMultipleChoiceExpandedLabelAttributesSetByCallable() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', array('&a', '&c'), array( + 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b', 'Choice&C' => '&c'), + 'choice_label_attr' => function ($choice, $key, $value) { + return '&b' === $choice ? array('class' => 'foo&bar') : array(); + }, + 'multiple' => true, + 'expanded' => true, + 'required' => true, + )); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/div + [ + ./div + [@class="checkbox"] + [ + ./label + [.=" [trans]Choice&A[/trans]"] + [ + ./input[@type="checkbox"][@name="name[]"][@id="name_0"][@checked][not(@required)] + ] + ] + /following-sibling::div + [@class="checkbox"] + [ + ./label + [@class="foo&bar"] + [.=" [trans]Choice&B[/trans]"] + [ + ./input[@type="checkbox"][@name="name[]"][@id="name_1"][not(@checked)][not(@required)] + ] + ] + /following-sibling::div + [@class="checkbox"] + [ + ./label + [.=" [trans]Choice&C[/trans]"] + [ + ./input[@type="checkbox"][@name="name[]"][@id="name_2"][@checked][not(@required)] + ] + ] + /following-sibling::input[@type="hidden"][@id="name__token"] + ] +' + ); + } + public function testCountry() { $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\CountryType', 'AT'); diff --git a/src/Symfony/Component/Form/Tests/AbstractLayoutTest.php b/src/Symfony/Component/Form/Tests/AbstractLayoutTest.php index 4b563ab8bfa36..5d946b6380792 100644 --- a/src/Symfony/Component/Form/Tests/AbstractLayoutTest.php +++ b/src/Symfony/Component/Form/Tests/AbstractLayoutTest.php @@ -1205,6 +1205,54 @@ public function testSingleChoiceExpandedAttributesSetByCallable() ); } + public function testSingleChoiceExpandedLabelAttributes() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', '&a', array( + 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'), + 'choice_label_attr' => array('class' => 'foo&bar'), + 'multiple' => false, + 'expanded' => true, + )); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/div + [ + ./input[@type="radio"][@name="name"][@id="name_0"][@value="&a"][@checked] + /following-sibling::label[@for="name_0"][@class="foo&bar required"][.="[trans]Choice&A[/trans]"] + /following-sibling::input[@type="radio"][@name="name"][@id="name_1"][@value="&b"][not(@checked)] + /following-sibling::label[@for="name_1"][@class="foo&bar required"][.="[trans]Choice&B[/trans]"] + /following-sibling::input[@type="hidden"][@id="name__token"] + ] + [count(./input)=3] +' + ); + } + + public function testSingleChoiceExpandedLabelAttributesSetByCallable() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', '&a', array( + 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b'), + 'choice_label_attr' => function ($choice, $key, $value) { + return '&b' === $choice ? array('class' => 'foo&bar') : array(); + }, + 'multiple' => false, + 'expanded' => true, + )); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/div + [ + ./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"][@class="foo&bar required"][.="[trans]Choice&B[/trans]"] + /following-sibling::input[@type="hidden"][@id="name__token"] + ] + [count(./input)=3] +' + ); + } + public function testSingleChoiceExpandedWithPlaceholder() { $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', '&a', array( @@ -1414,6 +1462,60 @@ public function testMultipleChoiceExpandedAttributesSetByCallable() ); } + public function testMultipleChoiceExpandedLabelAttributes() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', array('&a', '&c'), array( + 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b', 'Choice&C' => '&c'), + 'choice_label_attr' => array('class' => 'foo&bar'), + 'multiple' => true, + 'expanded' => true, + 'required' => true, + )); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/div + [ + ./input[@type="checkbox"][@name="name[]"][@id="name_0"][@checked][not(@required)] + /following-sibling::label[@for="name_0"][@class="foo&bar"][.="[trans]Choice&A[/trans]"] + /following-sibling::input[@type="checkbox"][@name="name[]"][@id="name_1"][not(@checked)][not(@required)] + /following-sibling::label[@for="name_1"][@class="foo&bar"][.="[trans]Choice&B[/trans]"] + /following-sibling::input[@type="checkbox"][@name="name[]"][@id="name_2"][@checked][not(@required)] + /following-sibling::label[@for="name_2"][@class="foo&bar"][.="[trans]Choice&C[/trans]"] + /following-sibling::input[@type="hidden"][@id="name__token"] + ] + [count(./input)=4] +' + ); + } + + public function testMultipleChoiceExpandedLabelAttributesSetByCallable() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', array('&a', '&c'), array( + 'choices' => array('Choice&A' => '&a', 'Choice&B' => '&b', 'Choice&C' => '&c'), + 'choice_label_attr' => function ($choice, $key, $value) { + return '&b' === $choice ? array('class' => 'foo&bar') : array(); + }, + 'multiple' => true, + 'expanded' => true, + 'required' => true, + )); + + $this->assertWidgetMatchesXpath($form->createView(), array(), +'/div + [ + ./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"][@class="foo&bar"][.="[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] +' + ); + } + public function testCountry() { $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\CountryType', 'AT'); From 0fe70970c47e862551178681d29effcf1799c9d8 Mon Sep 17 00:00:00 2001 From: HeahDude Date: Sun, 26 Mar 2017 19:51:42 +0200 Subject: [PATCH 4/4] Fixed choice_label_attr option BC layer --- .../Factory/CachingFactoryDecorator.php | 43 +++-------- .../Factory/DefaultChoiceListFactory.php | 12 +-- .../ExpandedChoiceListFactoryInterface.php | 76 ------------------- .../Factory/PropertyAccessDecorator.php | 18 +---- .../Form/Extension/Core/Type/ChoiceType.php | 17 ++--- 5 files changed, 22 insertions(+), 144 deletions(-) delete mode 100644 src/Symfony/Component/Form/ChoiceList/Factory/ExpandedChoiceListFactoryInterface.php diff --git a/src/Symfony/Component/Form/ChoiceList/Factory/CachingFactoryDecorator.php b/src/Symfony/Component/Form/ChoiceList/Factory/CachingFactoryDecorator.php index ebed4728b89d0..d921094e3808f 100644 --- a/src/Symfony/Component/Form/ChoiceList/Factory/CachingFactoryDecorator.php +++ b/src/Symfony/Component/Form/ChoiceList/Factory/CachingFactoryDecorator.php @@ -20,7 +20,7 @@ * * @author Bernhard Schussek */ -class CachingFactoryDecorator implements ExpandedChoiceListFactoryInterface +class CachingFactoryDecorator implements ChoiceListFactoryInterface { /** * @var ChoiceListFactoryInterface @@ -152,43 +152,22 @@ public function createListFromLoader(ChoiceLoaderInterface $loader, $value = nul /** * {@inheritdoc} */ - public function createView(ChoiceListInterface $list, $preferredChoices = null, $label = null, $index = null, $groupBy = null, $attr = null) - { - return $this->createExpandedView($list, $preferredChoices, $label, $index, $groupBy, $attr); - } - - /** - * {@inheritdoc} - */ - public function createExpandedView(ChoiceListInterface $list, $preferredChoices = null, $label = null, $index = null, $groupBy = null, $attr = null, $labelAttr = null) + public function createView(ChoiceListInterface $list, $preferredChoices = null, $label = null, $index = null, $groupBy = null, $attr = null, $labelAttr = null) { // The input is not validated on purpose. This way, the decorated // factory may decide which input to accept and which not. $hash = self::generateHash(array($list, $preferredChoices, $label, $index, $groupBy, $attr)); if (!isset($this->views[$hash])) { - // BC layer - if ($this->decoratedFactory instanceof ExpandedChoiceListFactoryInterface) { - $choiceView = $this->decoratedFactory->createExpandedView( - $list, - $preferredChoices, - $label, - $index, - $groupBy, - $attr, - $labelAttr - ); - } else { - $choiceView = $this->decoratedFactory->createView( - $list, - $preferredChoices, - $label, - $index, - $groupBy, - $attr - ); - } - $this->views[$hash] = $choiceView; + $this->views[$hash] = $this->decoratedFactory->createView( + $list, + $preferredChoices, + $label, + $index, + $groupBy, + $attr, + $labelAttr + ); } return $this->views[$hash]; diff --git a/src/Symfony/Component/Form/ChoiceList/Factory/DefaultChoiceListFactory.php b/src/Symfony/Component/Form/ChoiceList/Factory/DefaultChoiceListFactory.php index e5d9cc49f1df7..356956fdd459e 100644 --- a/src/Symfony/Component/Form/ChoiceList/Factory/DefaultChoiceListFactory.php +++ b/src/Symfony/Component/Form/ChoiceList/Factory/DefaultChoiceListFactory.php @@ -24,7 +24,7 @@ * * @author Bernhard Schussek */ -class DefaultChoiceListFactory implements ExpandedChoiceListFactoryInterface +class DefaultChoiceListFactory implements ChoiceListFactoryInterface { /** * {@inheritdoc} @@ -45,15 +45,7 @@ public function createListFromLoader(ChoiceLoaderInterface $loader, $value = nul /** * {@inheritdoc} */ - public function createView(ChoiceListInterface $list, $preferredChoices = null, $label = null, $index = null, $groupBy = null, $attr = null) - { - return $this->createExpandedView($list, $preferredChoices, $label, $index, $groupBy, $attr); - } - - /** - * {@inheritdoc} - */ - public function createExpandedView(ChoiceListInterface $list, $preferredChoices = null, $label = null, $index = null, $groupBy = null, $attr = null, $labelAttr = null) + public function createView(ChoiceListInterface $list, $preferredChoices = null, $label = null, $index = null, $groupBy = null, $attr = null, $labelAttr = null) { $preferredViews = array(); $otherViews = array(); diff --git a/src/Symfony/Component/Form/ChoiceList/Factory/ExpandedChoiceListFactoryInterface.php b/src/Symfony/Component/Form/ChoiceList/Factory/ExpandedChoiceListFactoryInterface.php deleted file mode 100644 index 24d9299bb3283..0000000000000 --- a/src/Symfony/Component/Form/ChoiceList/Factory/ExpandedChoiceListFactoryInterface.php +++ /dev/null @@ -1,76 +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\ChoiceList\Factory; - -use Symfony\Component\Form\ChoiceList\ChoiceListInterface; -use Symfony\Component\Form\ChoiceList\View\ChoiceListView; - -/** - * Creates {@link ChoiceListInterface} instances. - * - * Provides a BC layer for 3.x. - * - * To be deprecated in 3.4 and removed in 4.0, in favor of {@link ChoiceListFactoryInterface::createview()} - * - * @author Bernhard Schussek - * @author Jules Pietri - */ -interface ExpandedChoiceListFactoryInterface extends ChoiceListFactoryInterface -{ - /** - * Creates a view for the given choice list. - * - * Callables may be passed for all optional arguments. The callables receive - * the choice as first and the array key as the second argument. - * - * * The callable for the label and the name should return the generated - * label/choice name. - * * The callable for the preferred choices should return true or false, - * depending on whether the choice should be preferred or not. - * * The callable for the grouping should return the group name or null if - * a choice should not be grouped. - * * The callable for the attributes should return an array of HTML - * attributes that will be inserted in the tag of the choice. - * * The callable for the label attributes should return an array of HTML - * attributes that will be inserted in the tag of the choice. - * - * If no callable is passed, the labels will be generated from the choice - * keys. The view indices will be generated using an incrementing integer - * by default. - * - * The preferred choices can also be passed as array. Each choice that is - * contained in that array will be marked as preferred. - * - * The attributes can be passed as multi-dimensional array. The keys should - * match the keys of the choices. The values should be arrays of HTML - * attributes that should be added to the respective choice. - * - * The label attributes can be passed as an array. It will be used for - * each choice. - * - * @param ChoiceListInterface $list The choice list - * @param null|array|callable $preferredChoices The preferred choices - * @param null|callable $label The callable generating the - * choice labels - * @param null|callable $index The callable generating the - * view indices - * @param null|callable $groupBy The callable generating the - * group names - * @param null|array|callable $attr The callable generating the - * HTML attributes - * @param null|array|callable $labelAttr The callable generating the - * HTML attributes - * - * @return ChoiceListView The choice list view - */ - public function createExpandedView(ChoiceListInterface $list, $preferredChoices = null, $label = null, $index = null, $groupBy = null, $attr = null, $labelAttr = null); -} diff --git a/src/Symfony/Component/Form/ChoiceList/Factory/PropertyAccessDecorator.php b/src/Symfony/Component/Form/ChoiceList/Factory/PropertyAccessDecorator.php index 0e5faf994953f..f555e5f3bc4d4 100644 --- a/src/Symfony/Component/Form/ChoiceList/Factory/PropertyAccessDecorator.php +++ b/src/Symfony/Component/Form/ChoiceList/Factory/PropertyAccessDecorator.php @@ -39,7 +39,7 @@ * * @author Bernhard Schussek */ -class PropertyAccessDecorator implements ExpandedChoiceListFactoryInterface +class PropertyAccessDecorator implements ChoiceListFactoryInterface { /** * @var ChoiceListFactoryInterface @@ -139,11 +139,6 @@ public function createListFromLoader(ChoiceLoaderInterface $loader, $value = nul return $this->decoratedFactory->createListFromLoader($loader, $value); } - public function createView(ChoiceListInterface $list, $preferredChoices = null, $label = null, $index = null, $groupBy = null, $attr = null) - { - return $this->createExpandedView($list, $preferredChoices, $label, $index, $groupBy, $attr); - } - /** * {@inheritdoc} * @@ -157,7 +152,7 @@ public function createView(ChoiceListInterface $list, $preferredChoices = null, * * @return ChoiceListView The choice list view */ - public function createExpandedView(ChoiceListInterface $list, $preferredChoices = null, $label = null, $index = null, $groupBy = null, $attr = null, $labelAttr = null) + public function createView(ChoiceListInterface $list, $preferredChoices = null, $label = null, $index = null, $groupBy = null, $attr = null, $labelAttr = null) { $accessor = $this->propertyAccessor; @@ -231,13 +226,6 @@ public function createExpandedView(ChoiceListInterface $list, $preferredChoices }; } - // BC Layer - if ($this->decoratedFactory instanceof ExpandedChoiceListFactoryInterface) { - $choiceView = $this->decoratedFactory->createExpandedView($list, $preferredChoices, $label, $index, $groupBy, $attr, $labelAttr); - } else { - $choiceView = $this->decoratedFactory->createView($list, $preferredChoices, $label, $index, $groupBy, $attr); - } - - return $choiceView; + return $this->decoratedFactory->createView($list, $preferredChoices, $label, $index, $groupBy, $attr, $labelAttr); } } diff --git a/src/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php b/src/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php index f9ce0123450bc..d4d27a172c346 100644 --- a/src/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php +++ b/src/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php @@ -452,16 +452,10 @@ private function createChoiceList(array $options) private function createChoiceListView(ChoiceListInterface $choiceList, array $options) { - if ($this->choiceListFactory instanceof ExpandedChoiceListFactoryInterface) { - return $this->choiceListFactory->createExpandedView( - $choiceList, - $options['preferred_choices'], - $options['choice_label'], - $options['choice_name'], - $options['group_by'], - $options['choice_attr'], - $options['choice_label_attr'] - ); + // BC + $refMethod = new \ReflectionMethod($this->choiceListFactory, 'createView'); + if (6 > $refMethod->getNumberOfParameters()) { + @trigger_error(sprintf('Not passing a "$labelAttr" as sixth argument of "%s" is deprecated since version 3.3 and will trigger an error in 4.0.', $refMethod->getNamespaceName())); } return $this->choiceListFactory->createView( @@ -470,7 +464,8 @@ private function createChoiceListView(ChoiceListInterface $choiceList, array $op $options['choice_label'], $options['choice_name'], $options['group_by'], - $options['choice_attr'] + $options['choice_attr'], + $options['choice_label_attr'] ); } }