diff --git a/src/Symfony/Component/Form/ChoiceList/ArrayChoiceList.php b/src/Symfony/Component/Form/ChoiceList/ArrayChoiceList.php index 156735b817..b1e6b3bf06 100644 --- a/src/Symfony/Component/Form/ChoiceList/ArrayChoiceList.php +++ b/src/Symfony/Component/Form/ChoiceList/ArrayChoiceList.php @@ -74,6 +74,12 @@ class ArrayChoiceList implements ChoiceListInterface $choices = iterator_to_array($choices); } + if (null === $value && $this->castableToString($choices)) { + $value = function ($choice) { + return (string) $choice; + }; + } + if (null !== $value) { // If a deterministic value generator was passed, use it later $this->valueCallback = $value; @@ -207,4 +213,35 @@ class ArrayChoiceList implements ChoiceListInterface $structuredValues[$key] = $choiceValue; } } + + /** + * Checks whether the given choices can be cast to strings without + * generating duplicates. + * + * @param array $choices The choices. + * @param array|null $cache The cache for previously checked entries. Internal + * + * @return bool Returns true if the choices can be cast to strings and + * false otherwise. + */ + private function castableToString(array $choices, array &$cache = array()) + { + foreach ($choices as $choice) { + if (is_array($choice)) { + if (!$this->castableToString($choice, $cache)) { + return false; + } + + continue; + } elseif (!is_scalar($choice)) { + return false; + } elseif (isset($cache[(string) $choice])) { + return false; + } + + $cache[(string) $choice] = true; + } + + return true; + } } diff --git a/src/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php b/src/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php index c325521d36..de20b174ab 100644 --- a/src/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php +++ b/src/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php @@ -281,6 +281,14 @@ class ChoiceType extends AbstractType return $choiceListFactory->createListFromChoices($choices, $options['choice_value']); }; + $choicesAsValuesNormalizer = function (Options $options, $choicesAsValues) { + if (true !== $choicesAsValues) { + @trigger_error('The value "false" for the "choices_as_values" option is deprecated since version 2.8 and will not be supported anymore in 3.0. Set this option to "true" and flip the contents of the "choices" option instead.', E_USER_DEPRECATED); + } + + return $choicesAsValues; + }; + $placeholderNormalizer = function (Options $options, $placeholder) { if (!is_object($options['empty_value']) || !$options['empty_value'] instanceof \Exception) { @trigger_error('The form option "empty_value" is deprecated since version 2.6 and will be removed in 3.0. Use "placeholder" instead.', E_USER_DEPRECATED); @@ -343,6 +351,7 @@ class ChoiceType extends AbstractType $resolver->setNormalizer('choice_list', $choiceListNormalizer); $resolver->setNormalizer('placeholder', $placeholderNormalizer); $resolver->setNormalizer('choice_translation_domain', $choiceTranslationDomainNormalizer); + $resolver->setNormalizer('choices_as_values', $choicesAsValuesNormalizer); $resolver->setAllowedTypes('choice_list', array('null', 'Symfony\Component\Form\ChoiceList\ChoiceListInterface', 'Symfony\Component\Form\Extension\Core\ChoiceList\ChoiceListInterface')); $resolver->setAllowedTypes('choices', array('null', 'array', '\Traversable')); diff --git a/src/Symfony/Component/Form/Extension/Core/Type/CountryType.php b/src/Symfony/Component/Form/Extension/Core/Type/CountryType.php index 45da8d509a..2f114d9aa2 100644 --- a/src/Symfony/Component/Form/Extension/Core/Type/CountryType.php +++ b/src/Symfony/Component/Form/Extension/Core/Type/CountryType.php @@ -23,7 +23,8 @@ class CountryType extends AbstractType public function configureOptions(OptionsResolver $resolver) { $resolver->setDefaults(array( - 'choices' => Intl::getRegionBundle()->getCountryNames(), + 'choices' => array_flip(Intl::getRegionBundle()->getCountryNames()), + 'choices_as_values' => true, 'choice_translation_domain' => false, )); } diff --git a/src/Symfony/Component/Form/Extension/Core/Type/CurrencyType.php b/src/Symfony/Component/Form/Extension/Core/Type/CurrencyType.php index 7a26316e01..1608890d4a 100644 --- a/src/Symfony/Component/Form/Extension/Core/Type/CurrencyType.php +++ b/src/Symfony/Component/Form/Extension/Core/Type/CurrencyType.php @@ -23,7 +23,8 @@ class CurrencyType extends AbstractType public function configureOptions(OptionsResolver $resolver) { $resolver->setDefaults(array( - 'choices' => Intl::getCurrencyBundle()->getCurrencyNames(), + 'choices' => array_flip(Intl::getCurrencyBundle()->getCurrencyNames()), + 'choices_as_values' => true, 'choice_translation_domain' => false, )); } diff --git a/src/Symfony/Component/Form/Extension/Core/Type/DateType.php b/src/Symfony/Component/Form/Extension/Core/Type/DateType.php index df5835b8b3..921b24ff0f 100644 --- a/src/Symfony/Component/Form/Extension/Core/Type/DateType.php +++ b/src/Symfony/Component/Form/Extension/Core/Type/DateType.php @@ -93,12 +93,15 @@ class DateType extends AbstractType if ('choice' === $options['widget']) { // Only pass a subset of the options to children $yearOptions['choices'] = $this->formatTimestamps($formatter, '/y+/', $this->listYears($options['years'])); + $yearOptions['choices_as_values'] = true; $yearOptions['placeholder'] = $options['placeholder']['year']; $yearOptions['choice_translation_domain'] = $options['choice_translation_domain']['year']; $monthOptions['choices'] = $this->formatTimestamps($formatter, '/[M|L]+/', $this->listMonths($options['months'])); + $monthOptions['choices_as_values'] = true; $monthOptions['placeholder'] = $options['placeholder']['month']; $monthOptions['choice_translation_domain'] = $options['choice_translation_domain']['month']; $dayOptions['choices'] = $this->formatTimestamps($formatter, '/d+/', $this->listDays($options['days'])); + $dayOptions['choices_as_values'] = true; $dayOptions['placeholder'] = $options['placeholder']['day']; $dayOptions['choice_translation_domain'] = $options['choice_translation_domain']['day']; } @@ -297,6 +300,7 @@ class DateType extends AbstractType { $pattern = $formatter->getPattern(); $timezone = $formatter->getTimezoneId(); + $formattedTimestamps = array(); if ($setTimeZone = PHP_VERSION_ID >= 50500 || method_exists($formatter, 'setTimeZone')) { $formatter->setTimeZone('UTC'); @@ -307,8 +311,8 @@ class DateType extends AbstractType if (preg_match($regex, $pattern, $matches)) { $formatter->setPattern($matches[0]); - foreach ($timestamps as $key => $timestamp) { - $timestamps[$key] = $formatter->format($timestamp); + foreach ($timestamps as $timestamp => $choice) { + $formattedTimestamps[$formatter->format($timestamp)] = $choice; } // I'd like to clone the formatter above, but then we get a @@ -322,7 +326,7 @@ class DateType extends AbstractType $formatter->setTimeZoneId($timezone); } - return $timestamps; + return $formattedTimestamps; } private function listYears(array $years) @@ -331,7 +335,7 @@ class DateType extends AbstractType foreach ($years as $year) { if (false !== $y = gmmktime(0, 0, 0, 6, 15, $year)) { - $result[$year] = $y; + $result[$y] = $year; } } @@ -343,7 +347,7 @@ class DateType extends AbstractType $result = array(); foreach ($months as $month) { - $result[$month] = gmmktime(0, 0, 0, $month, 15); + $result[gmmktime(0, 0, 0, $month, 15)] = $month; } return $result; @@ -354,7 +358,7 @@ class DateType extends AbstractType $result = array(); foreach ($days as $day) { - $result[$day] = gmmktime(0, 0, 0, 5, $day); + $result[gmmktime(0, 0, 0, 5, $day)] = $day; } return $result; diff --git a/src/Symfony/Component/Form/Extension/Core/Type/LanguageType.php b/src/Symfony/Component/Form/Extension/Core/Type/LanguageType.php index 6fc6031af1..da5cbc75e4 100644 --- a/src/Symfony/Component/Form/Extension/Core/Type/LanguageType.php +++ b/src/Symfony/Component/Form/Extension/Core/Type/LanguageType.php @@ -23,7 +23,8 @@ class LanguageType extends AbstractType public function configureOptions(OptionsResolver $resolver) { $resolver->setDefaults(array( - 'choices' => Intl::getLanguageBundle()->getLanguageNames(), + 'choices' => array_flip(Intl::getLanguageBundle()->getLanguageNames()), + 'choices_as_values' => true, 'choice_translation_domain' => false, )); } diff --git a/src/Symfony/Component/Form/Extension/Core/Type/LocaleType.php b/src/Symfony/Component/Form/Extension/Core/Type/LocaleType.php index a6d42f8c91..25ae92ded9 100644 --- a/src/Symfony/Component/Form/Extension/Core/Type/LocaleType.php +++ b/src/Symfony/Component/Form/Extension/Core/Type/LocaleType.php @@ -23,7 +23,8 @@ class LocaleType extends AbstractType public function configureOptions(OptionsResolver $resolver) { $resolver->setDefaults(array( - 'choices' => Intl::getLocaleBundle()->getLocaleNames(), + 'choices' => array_flip(Intl::getLocaleBundle()->getLocaleNames()), + 'choices_as_values' => true, 'choice_translation_domain' => false, )); } diff --git a/src/Symfony/Component/Form/Extension/Core/Type/TimeType.php b/src/Symfony/Component/Form/Extension/Core/Type/TimeType.php index ed9132863a..1ab2030b1d 100644 --- a/src/Symfony/Component/Form/Extension/Core/Type/TimeType.php +++ b/src/Symfony/Component/Form/Extension/Core/Type/TimeType.php @@ -63,20 +63,22 @@ class TimeType extends AbstractType $hours = $minutes = array(); foreach ($options['hours'] as $hour) { - $hours[$hour] = str_pad($hour, 2, '0', STR_PAD_LEFT); + $hours[str_pad($hour, 2, '0', STR_PAD_LEFT)] = $hour; } // Only pass a subset of the options to children $hourOptions['choices'] = $hours; + $hourOptions['choices_as_values'] = true; $hourOptions['placeholder'] = $options['placeholder']['hour']; $hourOptions['choice_translation_domain'] = $options['choice_translation_domain']['hour']; if ($options['with_minutes']) { foreach ($options['minutes'] as $minute) { - $minutes[$minute] = str_pad($minute, 2, '0', STR_PAD_LEFT); + $minutes[str_pad($minute, 2, '0', STR_PAD_LEFT)] = $minute; } $minuteOptions['choices'] = $minutes; + $minuteOptions['choices_as_values'] = true; $minuteOptions['placeholder'] = $options['placeholder']['minute']; $minuteOptions['choice_translation_domain'] = $options['choice_translation_domain']['minute']; } @@ -85,10 +87,11 @@ class TimeType extends AbstractType $seconds = array(); foreach ($options['seconds'] as $second) { - $seconds[$second] = str_pad($second, 2, '0', STR_PAD_LEFT); + $seconds[str_pad($second, 2, '0', STR_PAD_LEFT)] = $second; } $secondOptions['choices'] = $seconds; + $secondOptions['choices_as_values'] = true; $secondOptions['placeholder'] = $options['placeholder']['second']; $secondOptions['choice_translation_domain'] = $options['choice_translation_domain']['second']; } diff --git a/src/Symfony/Component/Form/Extension/Core/Type/TimezoneType.php b/src/Symfony/Component/Form/Extension/Core/Type/TimezoneType.php index 3277a18386..a3ec7bf744 100644 --- a/src/Symfony/Component/Form/Extension/Core/Type/TimezoneType.php +++ b/src/Symfony/Component/Form/Extension/Core/Type/TimezoneType.php @@ -23,13 +23,21 @@ class TimezoneType extends AbstractType */ private static $timezones; + /** + * Stores the available timezone choices. + * + * @var array + */ + private static $flippedTimezones; + /** * {@inheritdoc} */ public function configureOptions(OptionsResolver $resolver) { $resolver->setDefaults(array( - 'choices' => self::getTimezones(), + 'choices' => self::getFlippedTimezones(), + 'choices_as_values' => true, 'choice_translation_domain' => false, )); } @@ -93,4 +101,40 @@ class TimezoneType extends AbstractType return static::$timezones; } + + /** + * Returns the timezone choices. + * + * The choices are generated from the ICU function + * \DateTimeZone::listIdentifiers(). They are cached during a single request, + * so multiple timezone fields on the same page don't lead to unnecessary + * overhead. + * + * @return array The timezone choices + */ + private static function getFlippedTimezones() + { + if (null === self::$timezones) { + self::$timezones = array(); + + foreach (\DateTimeZone::listIdentifiers() as $timezone) { + $parts = explode('/', $timezone); + + if (count($parts) > 2) { + $region = $parts[0]; + $name = $parts[1].' - '.$parts[2]; + } elseif (count($parts) > 1) { + $region = $parts[0]; + $name = $parts[1]; + } else { + $region = 'Other'; + $name = $parts[0]; + } + + self::$timezones[$region][str_replace('_', ' ', $name)] = $timezone; + } + } + + return self::$timezones; + } } diff --git a/src/Symfony/Component/Form/Tests/ChoiceList/ArrayChoiceListTest.php b/src/Symfony/Component/Form/Tests/ChoiceList/ArrayChoiceListTest.php index 50d4df8a9b..03cb7fce57 100644 --- a/src/Symfony/Component/Form/Tests/ChoiceList/ArrayChoiceListTest.php +++ b/src/Symfony/Component/Form/Tests/ChoiceList/ArrayChoiceListTest.php @@ -65,6 +65,40 @@ class ArrayChoiceListTest extends AbstractChoiceListTest $this->assertSame(array(1 => ':foo', 2 => ':baz'), $choiceList->getValuesForChoices(array(1 => 'foo', 2 => 'baz'))); } + public function testCreateChoiceListWithoutValueCallbackAndDuplicateFreeToStringChoices() + { + $choiceList = new ArrayChoiceList(array(2 => 'foo', 7 => 'bar', 10 => 123)); + + $this->assertSame(array('foo', 'bar', '123'), $choiceList->getValues()); + $this->assertSame(array('foo' => 'foo', 'bar' => 'bar', '123' => 123), $choiceList->getChoices()); + $this->assertSame(array('foo' => 2, 'bar' => 7, '123' => 10), $choiceList->getOriginalKeys()); + $this->assertSame(array(1 => 'foo', 2 => 123), $choiceList->getChoicesForValues(array(1 => 'foo', 2 => '123'))); + $this->assertSame(array(1 => 'foo', 2 => '123'), $choiceList->getValuesForChoices(array(1 => 'foo', 2 => 123))); + } + + public function testCreateChoiceListWithoutValueCallbackAndToStringDuplicates() + { + $choiceList = new ArrayChoiceList(array(2 => 'foo', 7 => '123', 10 => 123)); + + $this->assertSame(array('0', '1', '2'), $choiceList->getValues()); + $this->assertSame(array('0' => 'foo', '1' => '123', '2' => 123), $choiceList->getChoices()); + $this->assertSame(array('0' => 2, '1' => 7, '2' => 10), $choiceList->getOriginalKeys()); + $this->assertSame(array(1 => 'foo', 2 => 123), $choiceList->getChoicesForValues(array(1 => '0', 2 => '2'))); + $this->assertSame(array(1 => '0', 2 => '2'), $choiceList->getValuesForChoices(array(1 => 'foo', 2 => 123))); + } + + public function testCreateChoiceListWithoutValueCallbackAndMixedChoices() + { + $object = new \stdClass(); + $choiceList = new ArrayChoiceList(array(2 => 'foo', 5 => array(7 => '123'), 10 => $object)); + + $this->assertSame(array('0', '1', '2'), $choiceList->getValues()); + $this->assertSame(array('0' => 'foo', '1' => '123', '2' => $object), $choiceList->getChoices()); + $this->assertSame(array('0' => 2, '1' => 7, '2' => 10), $choiceList->getOriginalKeys()); + $this->assertSame(array(1 => 'foo', 2 => $object), $choiceList->getChoicesForValues(array(1 => '0', 2 => '2'))); + $this->assertSame(array(1 => '0', 2 => '2'), $choiceList->getValuesForChoices(array(1 => 'foo', 2 => $object))); + } + public function testCreateChoiceListWithGroupedChoices() { $choiceList = new ArrayChoiceList(array( @@ -72,15 +106,15 @@ class ArrayChoiceListTest extends AbstractChoiceListTest 'Group 2' => array('C' => 'c', 'D' => 'd'), )); - $this->assertSame(array('0', '1', '2', '3'), $choiceList->getValues()); + $this->assertSame(array('a', 'b', 'c', 'd'), $choiceList->getValues()); $this->assertSame(array( - 'Group 1' => array('A' => '0', 'B' => '1'), - 'Group 2' => array('C' => '2', 'D' => '3'), + 'Group 1' => array('A' => 'a', 'B' => 'b'), + 'Group 2' => array('C' => 'c', 'D' => 'd'), ), $choiceList->getStructuredValues()); - $this->assertSame(array(0 => 'a', 1 => 'b', 2 => 'c', 3 => 'd'), $choiceList->getChoices()); - $this->assertSame(array(0 => 'A', 1 => 'B', 2 => 'C', 3 => 'D'), $choiceList->getOriginalKeys()); - $this->assertSame(array(1 => 'a', 2 => 'b'), $choiceList->getChoicesForValues(array(1 => '0', 2 => '1'))); - $this->assertSame(array(1 => '0', 2 => '1'), $choiceList->getValuesForChoices(array(1 => 'a', 2 => 'b'))); + $this->assertSame(array('a' => 'a', 'b' => 'b', 'c' => 'c', 'd' => 'd'), $choiceList->getChoices()); + $this->assertSame(array('a' => 'A', 'b' => 'B', 'c' => 'C', 'd' => 'D'), $choiceList->getOriginalKeys()); + $this->assertSame(array(1 => 'a', 2 => 'b'), $choiceList->getChoicesForValues(array(1 => 'a', 2 => 'b'))); + $this->assertSame(array(1 => 'a', 2 => 'b'), $choiceList->getValuesForChoices(array(1 => 'a', 2 => 'b'))); } public function testCompareChoicesByIdentityByDefault() diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/ChoiceToValueTransformerTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/ChoiceToValueTransformerTest.php index c58d072f47..f60ef05abc 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/ChoiceToValueTransformerTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/ChoiceToValueTransformerTest.php @@ -20,7 +20,7 @@ class ChoiceToValueTransformerTest extends \PHPUnit_Framework_TestCase protected function setUp() { - $list = new ArrayChoiceList(array('', 0, 'X')); + $list = new ArrayChoiceList(array('', false, 'X')); $this->transformer = new ChoiceToValueTransformer($list); } @@ -35,7 +35,7 @@ class ChoiceToValueTransformerTest extends \PHPUnit_Framework_TestCase return array( // more extensive test set can be found in FormUtilTest array('', '0'), - array(0, '1'), + array(false, '1'), ); } @@ -53,7 +53,7 @@ class ChoiceToValueTransformerTest extends \PHPUnit_Framework_TestCase // values are expected to be valid choice keys already and stay // the same array('0', ''), - array('1', 0), + array('1', false), array('2', 'X'), ); } diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/ChoicesToValuesTransformerTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/ChoicesToValuesTransformerTest.php index a7dc40aca2..f7747aaccd 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/ChoicesToValuesTransformerTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/ChoicesToValuesTransformerTest.php @@ -20,7 +20,7 @@ class ChoicesToValuesTransformerTest extends \PHPUnit_Framework_TestCase protected function setUp() { - $list = new ArrayChoiceList(array('A', 'B', 'C')); + $list = new ArrayChoiceList(array('', false, 'X')); $this->transformer = new ChoicesToValuesTransformer($list); } @@ -31,7 +31,7 @@ class ChoicesToValuesTransformerTest extends \PHPUnit_Framework_TestCase public function testTransform() { - $in = array('A', 'B', 'C'); + $in = array('', false, 'X'); $out = array('0', '1', '2'); $this->assertSame($out, $this->transformer->transform($in)); @@ -54,7 +54,7 @@ class ChoicesToValuesTransformerTest extends \PHPUnit_Framework_TestCase { // values are expected to be valid choices and stay the same $in = array('0', '1', '2'); - $out = array('A', 'B', 'C'); + $out = array('', false, 'X'); $this->assertSame($out, $this->transformer->reverseTransform($in)); } 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 a8bf8edf12..732c2e8636 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Core/Type/ChoiceTypeTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Core/Type/ChoiceTypeTest.php @@ -18,14 +18,14 @@ use Symfony\Component\Form\Extension\Core\ChoiceList\ObjectChoiceList; class ChoiceTypeTest extends \Symfony\Component\Form\Test\TypeTestCase { private $choices = array( - 'a' => 'Bernhard', - 'b' => 'Fabien', - 'c' => 'Kris', - 'd' => 'Jon', - 'e' => 'Roman', + 'Bernhard' => 'a', + 'Fabien' => 'b', + 'Kris' => 'c', + 'Jon' => 'd', + 'Roman' => 'e', ); - private $numericChoices = array( + private $numericChoicesFlipped = array( 0 => 'Bernhard', 1 => 'Fabien', 2 => 'Kris', @@ -36,6 +36,18 @@ class ChoiceTypeTest extends \Symfony\Component\Form\Test\TypeTestCase private $objectChoices; protected $groupedChoices = array( + 'Symfony' => array( + 'Bernhard' => 'a', + 'Fabien' => 'b', + 'Kris' => 'c', + ), + 'Doctrine' => array( + 'Jon' => 'd', + 'Roman' => 'e', + ), + ); + + protected $groupedChoicesFlipped = array( 'Symfony' => array( 'a' => 'Bernhard', 'b' => 'Fabien', @@ -109,7 +121,9 @@ class ChoiceTypeTest extends \Symfony\Component\Form\Test\TypeTestCase public function testChoiceListAndChoicesCanBeEmpty() { - $this->factory->create('Symfony\Component\Form\Extension\Core\Type\ChoiceType'); + $this->factory->create('Symfony\Component\Form\Extension\Core\Type\ChoiceType', null, array( + 'choices_as_values' => true, + )); } public function testExpandedChoicesOptionsTurnIntoChildren() @@ -117,6 +131,20 @@ class ChoiceTypeTest extends \Symfony\Component\Form\Test\TypeTestCase $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\ChoiceType', null, array( 'expanded' => true, 'choices' => $this->choices, + 'choices_as_values' => true, + )); + + $this->assertCount(count($this->choices), $form, 'Each choice should become a new field'); + } + + /** + * @group legacy + */ + public function testExpandedFlippedChoicesOptionsTurnIntoChildren() + { + $form = $this->factory->create('choice', null, array( + 'expanded' => true, + 'choices' => array_flip($this->choices), )); $this->assertCount(count($this->choices), $form, 'Each choice should become a new field'); @@ -129,6 +157,7 @@ class ChoiceTypeTest extends \Symfony\Component\Form\Test\TypeTestCase 'expanded' => true, 'required' => false, 'choices' => $this->choices, + 'choices_as_values' => true, )); $this->assertTrue(isset($form['placeholder'])); @@ -142,6 +171,7 @@ class ChoiceTypeTest extends \Symfony\Component\Form\Test\TypeTestCase 'expanded' => true, 'required' => true, 'choices' => $this->choices, + 'choices_as_values' => true, )); $this->assertFalse(isset($form['placeholder'])); @@ -155,6 +185,7 @@ class ChoiceTypeTest extends \Symfony\Component\Form\Test\TypeTestCase 'expanded' => true, 'required' => false, 'choices' => $this->choices, + 'choices_as_values' => true, )); $this->assertFalse(isset($form['placeholder'])); @@ -168,9 +199,10 @@ class ChoiceTypeTest extends \Symfony\Component\Form\Test\TypeTestCase 'expanded' => true, 'required' => false, 'choices' => array( - '' => 'Empty', - 1 => 'Not empty', + 'Empty' => '', + 'Not empty' => 1, ), + 'choices_as_values' => true, )); $this->assertFalse(isset($form['placeholder'])); @@ -182,6 +214,29 @@ class ChoiceTypeTest extends \Symfony\Component\Form\Test\TypeTestCase $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\ChoiceType', null, array( 'expanded' => true, 'choices' => $this->groupedChoices, + 'choices_as_values' => true, + )); + + $flattened = array(); + foreach ($this->groupedChoices as $choices) { + $flattened = array_merge($flattened, array_keys($choices)); + } + + $this->assertCount($form->count(), $flattened, 'Each nested choice should become a new field, not the groups'); + + foreach ($flattened as $value => $choice) { + $this->assertTrue($form->has($value), 'Flattened choice is named after it\'s value'); + } + } + + /** + * @group legacy + */ + public function testExpandedChoicesFlippedOptionsAreFlattened() + { + $form = $this->factory->create('choice', null, array( + 'expanded' => true, + 'choices' => $this->groupedChoicesFlipped, )); $flattened = array(); @@ -229,6 +284,7 @@ class ChoiceTypeTest extends \Symfony\Component\Form\Test\TypeTestCase 'expanded' => true, 'required' => true, 'choices' => $this->choices, + 'choices_as_values' => true, )); foreach ($form as $child) { @@ -243,6 +299,7 @@ class ChoiceTypeTest extends \Symfony\Component\Form\Test\TypeTestCase 'expanded' => true, 'required' => true, 'choices' => $this->choices, + 'choices_as_values' => true, )); foreach ($form as $child) { @@ -257,6 +314,7 @@ class ChoiceTypeTest extends \Symfony\Component\Form\Test\TypeTestCase 'expanded' => true, 'required' => false, 'choices' => $this->choices, + 'choices_as_values' => true, )); foreach ($form as $child) { @@ -270,6 +328,7 @@ class ChoiceTypeTest extends \Symfony\Component\Form\Test\TypeTestCase 'multiple' => false, 'expanded' => false, 'choices' => $this->choices, + 'choices_as_values' => true, )); $form->submit('b'); @@ -285,6 +344,7 @@ class ChoiceTypeTest extends \Symfony\Component\Form\Test\TypeTestCase 'multiple' => false, 'expanded' => false, 'choices' => $this->choices, + 'choices_as_values' => true, )); $form->submit('foobar'); @@ -300,6 +360,7 @@ class ChoiceTypeTest extends \Symfony\Component\Form\Test\TypeTestCase 'multiple' => false, 'expanded' => false, 'choices' => $this->choices, + 'choices_as_values' => true, )); $form->submit(null); @@ -318,6 +379,7 @@ class ChoiceTypeTest extends \Symfony\Component\Form\Test\TypeTestCase 'multiple' => false, 'expanded' => false, 'choices' => array(), + 'choices_as_values' => true, )); $form->submit(null); @@ -333,6 +395,7 @@ class ChoiceTypeTest extends \Symfony\Component\Form\Test\TypeTestCase 'multiple' => false, 'expanded' => false, 'choices' => $this->choices, + 'choices_as_values' => true, )); $form->submit(''); @@ -348,8 +411,9 @@ class ChoiceTypeTest extends \Symfony\Component\Form\Test\TypeTestCase 'multiple' => false, 'expanded' => false, 'choices' => array( - 'EMPTY_CHOICE' => 'Empty', + 'Empty' => 'EMPTY_CHOICE', ), + 'choices_as_values' => true, 'choice_value' => function () { return ''; }, @@ -371,6 +435,7 @@ class ChoiceTypeTest extends \Symfony\Component\Form\Test\TypeTestCase 'multiple' => false, 'expanded' => false, 'choices' => array(), + 'choices_as_values' => true, )); $form->submit(''); @@ -386,6 +451,7 @@ class ChoiceTypeTest extends \Symfony\Component\Form\Test\TypeTestCase 'multiple' => false, 'expanded' => false, 'choices' => $this->choices, + 'choices_as_values' => true, )); $form->submit(false); @@ -404,6 +470,7 @@ class ChoiceTypeTest extends \Symfony\Component\Form\Test\TypeTestCase 'multiple' => false, 'expanded' => false, 'choices' => array(), + 'choices_as_values' => true, )); $form->submit(false); @@ -465,6 +532,7 @@ class ChoiceTypeTest extends \Symfony\Component\Form\Test\TypeTestCase 'multiple' => true, 'expanded' => false, 'choices' => $this->choices, + 'choices_as_values' => true, )); $form->submit(array('a', 'b')); @@ -480,6 +548,7 @@ class ChoiceTypeTest extends \Symfony\Component\Form\Test\TypeTestCase 'multiple' => true, 'expanded' => false, 'choices' => $this->choices, + 'choices_as_values' => true, )); $form->submit(array()); @@ -498,6 +567,7 @@ class ChoiceTypeTest extends \Symfony\Component\Form\Test\TypeTestCase 'multiple' => true, 'expanded' => false, 'choices' => array(), + 'choices_as_values' => true, )); $form->submit(array()); @@ -513,6 +583,7 @@ class ChoiceTypeTest extends \Symfony\Component\Form\Test\TypeTestCase 'multiple' => true, 'expanded' => false, 'choices' => $this->choices, + 'choices_as_values' => true, )); $form->submit('foobar'); @@ -528,6 +599,7 @@ class ChoiceTypeTest extends \Symfony\Component\Form\Test\TypeTestCase 'multiple' => true, 'expanded' => false, 'choices' => $this->choices, + 'choices_as_values' => true, )); $form->submit(array('a', 'foobar')); @@ -588,6 +660,7 @@ class ChoiceTypeTest extends \Symfony\Component\Form\Test\TypeTestCase 'expanded' => true, 'required' => true, 'choices' => $this->choices, + 'choices_as_values' => true, )); $form->submit('b'); @@ -616,6 +689,7 @@ class ChoiceTypeTest extends \Symfony\Component\Form\Test\TypeTestCase 'expanded' => true, 'required' => true, 'choices' => $this->choices, + 'choices_as_values' => true, )); $form->submit('foobar'); @@ -644,6 +718,7 @@ class ChoiceTypeTest extends \Symfony\Component\Form\Test\TypeTestCase 'expanded' => true, 'required' => false, 'choices' => $this->choices, + 'choices_as_values' => true, )); $form->submit('b'); @@ -674,6 +749,7 @@ class ChoiceTypeTest extends \Symfony\Component\Form\Test\TypeTestCase 'expanded' => true, 'required' => false, 'choices' => $this->choices, + 'choices_as_values' => true, )); $form->submit('foobar'); @@ -702,6 +778,7 @@ class ChoiceTypeTest extends \Symfony\Component\Form\Test\TypeTestCase 'expanded' => true, 'required' => true, 'choices' => $this->choices, + 'choices_as_values' => true, )); $form->submit(null); @@ -733,6 +810,7 @@ class ChoiceTypeTest extends \Symfony\Component\Form\Test\TypeTestCase 'expanded' => true, 'required' => true, 'choices' => array(), + 'choices_as_values' => true, )); $form->submit(null); @@ -750,6 +828,7 @@ class ChoiceTypeTest extends \Symfony\Component\Form\Test\TypeTestCase 'expanded' => true, 'required' => true, 'choices' => $this->choices, + 'choices_as_values' => true, )); $form->submit(''); @@ -781,6 +860,7 @@ class ChoiceTypeTest extends \Symfony\Component\Form\Test\TypeTestCase 'expanded' => true, 'required' => true, 'choices' => array(), + 'choices_as_values' => true, )); $form->submit(''); @@ -798,6 +878,7 @@ class ChoiceTypeTest extends \Symfony\Component\Form\Test\TypeTestCase 'expanded' => true, 'required' => true, 'choices' => $this->choices, + 'choices_as_values' => true, )); $form->submit(false); @@ -829,6 +910,7 @@ class ChoiceTypeTest extends \Symfony\Component\Form\Test\TypeTestCase 'expanded' => true, 'required' => true, 'choices' => array(), + 'choices_as_values' => true, )); $form->submit(false); @@ -846,6 +928,7 @@ class ChoiceTypeTest extends \Symfony\Component\Form\Test\TypeTestCase 'expanded' => true, 'required' => false, 'choices' => $this->choices, + 'choices_as_values' => true, )); $form->submit(null); @@ -879,6 +962,7 @@ class ChoiceTypeTest extends \Symfony\Component\Form\Test\TypeTestCase 'expanded' => true, 'required' => false, 'choices' => array(), + 'choices_as_values' => true, )); $form->submit(null); @@ -896,6 +980,7 @@ class ChoiceTypeTest extends \Symfony\Component\Form\Test\TypeTestCase 'expanded' => true, 'required' => false, 'choices' => $this->choices, + 'choices_as_values' => true, )); $form->submit(''); @@ -929,6 +1014,7 @@ class ChoiceTypeTest extends \Symfony\Component\Form\Test\TypeTestCase 'expanded' => true, 'required' => false, 'choices' => array(), + 'choices_as_values' => true, )); $form->submit(''); @@ -946,6 +1032,7 @@ class ChoiceTypeTest extends \Symfony\Component\Form\Test\TypeTestCase 'expanded' => true, 'required' => false, 'choices' => $this->choices, + 'choices_as_values' => true, )); $form->submit(false); @@ -979,6 +1066,7 @@ class ChoiceTypeTest extends \Symfony\Component\Form\Test\TypeTestCase 'expanded' => true, 'required' => false, 'choices' => array(), + 'choices_as_values' => true, )); $form->submit(false); @@ -995,9 +1083,10 @@ class ChoiceTypeTest extends \Symfony\Component\Form\Test\TypeTestCase 'multiple' => false, 'expanded' => true, 'choices' => array( - '' => 'Empty', - 1 => 'Not empty', + 'Empty' => '', + 'Not empty' => 1, ), + 'choices_as_values' => true, )); $form->submit(''); @@ -1075,12 +1164,15 @@ class ChoiceTypeTest extends \Symfony\Component\Form\Test\TypeTestCase $this->assertNull($form[4]->getViewData()); } - public function testSubmitSingleExpandedNumericChoices() + /** + * @group legacy + */ + public function testSubmitSingleExpandedNumericChoicesFlipped() { $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\ChoiceType', null, array( 'multiple' => false, 'expanded' => true, - 'choices' => $this->numericChoices, + 'choices' => $this->numericChoicesFlipped, )); $form->submit('1'); @@ -1106,6 +1198,7 @@ class ChoiceTypeTest extends \Symfony\Component\Form\Test\TypeTestCase 'multiple' => true, 'expanded' => true, 'choices' => $this->choices, + 'choices_as_values' => true, )); $form->submit(array('a', 'c')); @@ -1133,6 +1226,7 @@ class ChoiceTypeTest extends \Symfony\Component\Form\Test\TypeTestCase 'multiple' => true, 'expanded' => true, 'choices' => $this->choices, + 'choices_as_values' => true, )); $form->submit('foobar'); @@ -1160,6 +1254,7 @@ class ChoiceTypeTest extends \Symfony\Component\Form\Test\TypeTestCase 'multiple' => true, 'expanded' => true, 'choices' => $this->choices, + 'choices_as_values' => true, )); $form->submit(array('a', 'foobar')); @@ -1187,6 +1282,7 @@ class ChoiceTypeTest extends \Symfony\Component\Form\Test\TypeTestCase 'multiple' => true, 'expanded' => true, 'choices' => $this->choices, + 'choices_as_values' => true, )); $form->submit(array()); @@ -1215,6 +1311,7 @@ class ChoiceTypeTest extends \Symfony\Component\Form\Test\TypeTestCase 'multiple' => true, 'expanded' => true, 'choices' => array(), + 'choices_as_values' => true, )); $form->submit(array()); @@ -1229,10 +1326,11 @@ class ChoiceTypeTest extends \Symfony\Component\Form\Test\TypeTestCase 'multiple' => true, 'expanded' => true, 'choices' => array( - '' => 'Empty', - 1 => 'Not Empty', - 2 => 'Not Empty 2', + 'Empty' => '', + 'Not Empty' => 1, + 'Not Empty 2' => 2, ), + 'choices_as_values' => true, )); $form->submit(array('', '2')); @@ -1312,12 +1410,15 @@ class ChoiceTypeTest extends \Symfony\Component\Form\Test\TypeTestCase $this->assertNull($form[4]->getViewData()); } + /** + * @group legacy + */ public function testSubmitMultipleExpandedNumericChoices() { $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\ChoiceType', null, array( 'multiple' => true, 'expanded' => true, - 'choices' => $this->numericChoices, + 'choices' => $this->numericChoicesFlipped, )); $form->submit(array('1', '2')); @@ -1373,16 +1474,18 @@ class ChoiceTypeTest extends \Symfony\Component\Form\Test\TypeTestCase $this->assertFalse($selectedChecker($view->vars['choices'][1]->value, $view->vars['value'])); } - /* + /** * We need this functionality to create choice fields for Boolean types, - * e.g. false => 'No', true => 'Yes' + * e.g. false => 'No', true => 'Yes'. + * + * @group legacy */ public function testSetDataSingleNonExpandedAcceptsBoolean() { $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\ChoiceType', null, array( 'multiple' => false, 'expanded' => false, - 'choices' => $this->numericChoices, + 'choices' => $this->numericChoicesFlipped, )); $form->setData(false); @@ -1392,12 +1495,15 @@ class ChoiceTypeTest extends \Symfony\Component\Form\Test\TypeTestCase $this->assertTrue($form->isSynchronized()); } + /** + * @group legacy + */ public function testSetDataMultipleNonExpandedAcceptsBoolean() { $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\ChoiceType', null, array( 'multiple' => true, 'expanded' => false, - 'choices' => $this->numericChoices, + 'choices' => $this->numericChoicesFlipped, )); $form->setData(array(false, true)); @@ -1411,6 +1517,7 @@ class ChoiceTypeTest extends \Symfony\Component\Form\Test\TypeTestCase { $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\ChoiceType', null, array( 'choices' => $this->choices, + 'choices_as_values' => true, )); $view = $form->createView(); @@ -1422,6 +1529,7 @@ class ChoiceTypeTest extends \Symfony\Component\Form\Test\TypeTestCase $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\ChoiceType', null, array( 'required' => false, 'choices' => $this->choices, + 'choices_as_values' => true, )); $view = $form->createView(); @@ -1433,6 +1541,7 @@ class ChoiceTypeTest extends \Symfony\Component\Form\Test\TypeTestCase $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\ChoiceType', null, array( 'multiple' => true, 'choices' => $this->choices, + 'choices_as_values' => true, )); $view = $form->createView(); @@ -1444,6 +1553,7 @@ class ChoiceTypeTest extends \Symfony\Component\Form\Test\TypeTestCase $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\ChoiceType', null, array( 'expanded' => true, 'choices' => $this->choices, + 'choices_as_values' => true, )); $view = $form->createView(); @@ -1454,6 +1564,7 @@ class ChoiceTypeTest extends \Symfony\Component\Form\Test\TypeTestCase { $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\ChoiceType', null, array( 'choices' => $this->choices, + 'choices_as_values' => true, )); $view = $form->createView(); @@ -1464,6 +1575,7 @@ class ChoiceTypeTest extends \Symfony\Component\Form\Test\TypeTestCase { $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\ChoiceType', null, array( 'choices' => $this->choices, + 'choices_as_values' => true, 'choice_translation_domain' => true, )); $view = $form->createView(); @@ -1475,6 +1587,7 @@ class ChoiceTypeTest extends \Symfony\Component\Form\Test\TypeTestCase { $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\ChoiceType', null, array( 'choices' => $this->choices, + 'choices_as_values' => true, 'translation_domain' => 'foo', )); $view = $form->createView(); @@ -1488,7 +1601,10 @@ class ChoiceTypeTest extends \Symfony\Component\Form\Test\TypeTestCase ->createNamedBuilder('parent', 'Symfony\Component\Form\Extension\Core\Type\FormType', null, array( 'translation_domain' => 'domain', )) - ->add('child', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType') + ->add('child', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', array( + 'choices' => array(), + 'choices_as_values' => true, + )) ->getForm() ->createView(); @@ -1501,6 +1617,7 @@ class ChoiceTypeTest extends \Symfony\Component\Form\Test\TypeTestCase 'multiple' => false, 'required' => true, 'choices' => $this->choices, + 'choices_as_values' => true, )); $view = $form->createView(); @@ -1513,6 +1630,7 @@ class ChoiceTypeTest extends \Symfony\Component\Form\Test\TypeTestCase 'multiple' => false, 'required' => false, 'choices' => $this->choices, + 'choices_as_values' => true, )); $view = $form->createView(); @@ -1530,6 +1648,7 @@ class ChoiceTypeTest extends \Symfony\Component\Form\Test\TypeTestCase 'required' => $required, 'placeholder' => $placeholder, 'choices' => $this->choices, + 'choices_as_values' => true, )); $view = $form->createView(); @@ -1549,6 +1668,7 @@ class ChoiceTypeTest extends \Symfony\Component\Form\Test\TypeTestCase 'required' => $required, 'empty_value' => $placeholder, 'choices' => $this->choices, + 'choices_as_values' => true, )); $view = $form->createView(); @@ -1568,7 +1688,8 @@ class ChoiceTypeTest extends \Symfony\Component\Form\Test\TypeTestCase 'expanded' => $expanded, 'required' => $required, 'placeholder' => $placeholder, - 'choices' => array('a' => 'A', '' => 'Empty'), + 'choices' => array('A' => 'a', 'Empty' => ''), + 'choices_as_values' => true, )); $view = $form->createView(); @@ -1622,9 +1743,10 @@ class ChoiceTypeTest extends \Symfony\Component\Form\Test\TypeTestCase public function testPassChoicesToView() { - $choices = array('a' => 'A', 'b' => 'B', 'c' => 'C', 'd' => 'D'); + $choices = array('A' => 'a', 'B' => 'b', 'C' => 'c', 'D' => 'd'); $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\ChoiceType', null, array( 'choices' => $choices, + 'choices_as_values' => true, )); $view = $form->createView(); @@ -1638,9 +1760,10 @@ class ChoiceTypeTest extends \Symfony\Component\Form\Test\TypeTestCase public function testPassPreferredChoicesToView() { - $choices = array('a' => 'A', 'b' => 'B', 'c' => 'C', 'd' => 'D'); + $choices = array('A' => 'a', 'B' => 'b', 'C' => 'c', 'D' => 'd'); $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\ChoiceType', null, array( 'choices' => $choices, + 'choices_as_values' => true, 'preferred_choices' => array('b', 'd'), )); $view = $form->createView(); @@ -1659,6 +1782,7 @@ class ChoiceTypeTest extends \Symfony\Component\Form\Test\TypeTestCase { $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\ChoiceType', null, array( 'choices' => $this->groupedChoices, + 'choices_as_values' => true, 'preferred_choices' => array('b', 'd'), )); $view = $form->createView(); @@ -1710,6 +1834,7 @@ class ChoiceTypeTest extends \Symfony\Component\Form\Test\TypeTestCase 'multiple' => true, 'expanded' => false, 'choices' => $this->choices, + 'choices_as_values' => true, )); $view = $form->createView(); @@ -1721,6 +1846,7 @@ class ChoiceTypeTest extends \Symfony\Component\Form\Test\TypeTestCase { $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', null, array( 'choices' => array(), + 'choices_as_values' => true, )); }