From 2fdec3ec8d7869ea21fdc4a7f774277c1988cea5 Mon Sep 17 00:00:00 2001 From: Marek Binkowski Date: Thu, 3 Jun 2021 23:17:03 +0200 Subject: [PATCH] [Form] Fix ChoiceType Extension to effectively set and use the translator ChoiceType constructor line order changed to set translator before early return; CoreExtension injects translator to ChoiceType --- .../Form/Extension/Core/CoreExtension.php | 2 +- .../Form/Extension/Core/Type/ChoiceType.php | 10 +-- .../Core/Type/ChoiceTypeTranslationTest.php | 73 +++++++++++++++++++ 3 files changed, 79 insertions(+), 6 deletions(-) create mode 100644 src/Symfony/Component/Form/Tests/Extension/Core/Type/ChoiceTypeTranslationTest.php diff --git a/src/Symfony/Component/Form/Extension/Core/CoreExtension.php b/src/Symfony/Component/Form/Extension/Core/CoreExtension.php index 9c19603df003a..c6768b86b497a 100644 --- a/src/Symfony/Component/Form/Extension/Core/CoreExtension.php +++ b/src/Symfony/Component/Form/Extension/Core/CoreExtension.php @@ -45,7 +45,7 @@ protected function loadTypes() new Type\FormType($this->propertyAccessor), new Type\BirthdayType(), new Type\CheckboxType(), - new Type\ChoiceType($this->choiceListFactory), + new Type\ChoiceType($this->choiceListFactory, $this->translator), new Type\CollectionType(), new Type\CountryType(), new Type\DateIntervalType(), diff --git a/src/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php b/src/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php index e670ae9ac986c..b48d30b4f82ce 100644 --- a/src/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php +++ b/src/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php @@ -62,6 +62,11 @@ public function __construct(ChoiceListFactoryInterface $choiceListFactory = null ) ); + if (null !== $translator && !$translator instanceof TranslatorInterface) { + throw new \TypeError(sprintf('Argument 2 passed to "%s()" must be han instance of "%s", "%s" given.', __METHOD__, TranslatorInterface::class, \is_object($translator) ? \get_class($translator) : \gettype($translator))); + } + $this->translator = $translator; + // BC, to be removed in 6.0 if ($this->choiceListFactory instanceof CachingFactoryDecorator) { return; @@ -72,11 +77,6 @@ public function __construct(ChoiceListFactoryInterface $choiceListFactory = null if ($ref->getNumberOfParameters() < 3) { trigger_deprecation('symfony/form', '5.1', 'Not defining a third parameter "callable|null $filter" in "%s::%s()" is deprecated.', $ref->class, $ref->name); } - - if (null !== $translator && !$translator instanceof TranslatorInterface) { - throw new \TypeError(sprintf('Argument 2 passed to "%s()" must be han instance of "%s", "%s" given.', __METHOD__, TranslatorInterface::class, \is_object($translator) ? \get_class($translator) : \gettype($translator))); - } - $this->translator = $translator; } /** diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/Type/ChoiceTypeTranslationTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/Type/ChoiceTypeTranslationTest.php new file mode 100644 index 0000000000000..defb5dbe52e64 --- /dev/null +++ b/src/Symfony/Component/Form/Tests/Extension/Core/Type/ChoiceTypeTranslationTest.php @@ -0,0 +1,73 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Form\Tests\Extension\Core\Type; + +use Symfony\Component\Form\Extension\Core\CoreExtension; +use Symfony\Component\Form\Test\TypeTestCase; +use Symfony\Contracts\Translation\TranslatorInterface; + +class ChoiceTypeTranslationTest extends TypeTestCase +{ + public const TESTED_TYPE = 'Symfony\Component\Form\Extension\Core\Type\ChoiceType'; + + private $choices = [ + 'Bernhard' => 'a', + 'Fabien' => 'b', + 'Kris' => 'c', + 'Jon' => 'd', + 'Roman' => 'e', + ]; + + protected function getExtensions() + { + $translator = $this->createMock(TranslatorInterface::class); + $translator->expects($this->any())->method('trans') + ->willReturnCallback(function ($key, $params) { + return strtr(sprintf('Translation of: %s', $key), $params); + } + ); + + return array_merge(parent::getExtensions(), [new CoreExtension(null, null, $translator)]); + } + + public function testInvalidMessageAwarenessForMultiple() + { + $form = $this->factory->create(static::TESTED_TYPE, null, [ + 'multiple' => true, + 'expanded' => false, + 'choices' => $this->choices, + 'invalid_message' => 'You are not able to use value "{{ value }}"', + ]); + + $form->submit(['My invalid choice']); + $this->assertEquals( + "ERROR: Translation of: You are not able to use value \"My invalid choice\"\n", + (string) $form->getErrors(true) + ); + } + + public function testInvalidMessageAwarenessForMultipleWithoutScalarOrArrayViewData() + { + $form = $this->factory->create(static::TESTED_TYPE, null, [ + 'multiple' => true, + 'expanded' => false, + 'choices' => $this->choices, + 'invalid_message' => 'You are not able to use value "{{ value }}"', + ]); + + $form->submit(new \stdClass()); + $this->assertEquals( + "ERROR: Translation of: You are not able to use value \"stdClass\"\n", + (string) $form->getErrors(true) + ); + } +}