diff --git a/src/Symfony/Bridge/Twig/Resources/views/Form/form_div_layout.html.twig b/src/Symfony/Bridge/Twig/Resources/views/Form/form_div_layout.html.twig
index 0a5cd42cfd..ff8faad141 100644
--- a/src/Symfony/Bridge/Twig/Resources/views/Form/form_div_layout.html.twig
+++ b/src/Symfony/Bridge/Twig/Resources/views/Form/form_div_layout.html.twig
@@ -255,6 +255,17 @@
{{ block('form_widget_simple') }}
{%- endblock color_widget -%}
+{%- block week_widget -%}
+ {%- if widget == 'single_text' -%}
+ {{ block('form_widget_simple') }}
+ {%- else -%}
+ {%- set vars = widget == 'text' ? { 'attr': { 'size': 1 }} : {} -%}
+
+ {{ form_widget(form.year, vars) }}-{{ form_widget(form.week, vars) }}
+
+ {%- endif -%}
+{%- endblock week_widget -%}
+
{# Labels #}
{%- block form_label -%}
diff --git a/src/Symfony/Bridge/Twig/Tests/Extension/AbstractBootstrap3LayoutTest.php b/src/Symfony/Bridge/Twig/Tests/Extension/AbstractBootstrap3LayoutTest.php
index d7868b274b..6fb468b9dd 100644
--- a/src/Symfony/Bridge/Twig/Tests/Extension/AbstractBootstrap3LayoutTest.php
+++ b/src/Symfony/Bridge/Twig/Tests/Extension/AbstractBootstrap3LayoutTest.php
@@ -2722,6 +2722,104 @@ abstract class AbstractBootstrap3LayoutTest extends AbstractLayoutTest
[@name="name"]
[@class="my&class form-control"]
[@value="#0000ff"]
+'
+ );
+ }
+
+ public function testWeekSingleText()
+ {
+ $this->requiresFeatureSet(404);
+
+ $form = $this->factory->createNamed('holidays', 'Symfony\Component\Form\Extension\Core\Type\WeekType', '1970-W01', [
+ 'input' => 'string',
+ 'widget' => 'single_text',
+ ]);
+
+ $this->assertWidgetMatchesXpath($form->createView(), ['attr' => ['class' => 'my&class']],
+ '/input
+ [@type="week"]
+ [@name="holidays"]
+ [@class="my&class form-control"]
+ [@value="1970-W01"]
+ [not(@maxlength)]
+'
+ );
+ }
+
+ public function testWeekSingleTextNoHtml5()
+ {
+ $this->requiresFeatureSet(404);
+
+ $form = $this->factory->createNamed('holidays', 'Symfony\Component\Form\Extension\Core\Type\WeekType', '1970-W01', [
+ 'input' => 'string',
+ 'widget' => 'single_text',
+ 'html5' => false,
+ ]);
+
+ $this->assertWidgetMatchesXpath($form->createView(), ['attr' => ['class' => 'my&class']],
+ '/input
+ [@type="text"]
+ [@name="holidays"]
+ [@class="my&class form-control"]
+ [@value="1970-W01"]
+ [not(@maxlength)]
+'
+ );
+ }
+
+ public function testWeekChoices()
+ {
+ $this->requiresFeatureSet(404);
+
+ $data = ['year' => date('Y'), 'week' => 1];
+
+ $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\WeekType', $data, [
+ 'input' => 'array',
+ 'required' => false,
+ ]);
+
+ $this->assertWidgetMatchesXpath($form->createView(), ['attr' => ['class' => 'my&class']],
+ '/div
+ [@class="my&class"]
+ [
+ ./select
+ [@id="name_year"]
+ [@class="form-control"]
+ [./option[@value="'.$data['year'].'"][@selected="selected"]]
+ /following-sibling::select
+ [@id="name_week"]
+ [@class="form-control"]
+ [./option[@value="'.$data['week'].'"][@selected="selected"]]
+ ]
+ [count(.//select)=2]'
+ );
+ }
+
+ public function testWeekText()
+ {
+ $this->requiresFeatureSet(404);
+
+ $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\WeekType', '2000-W01', [
+ 'input' => 'string',
+ 'widget' => 'text',
+ ]);
+
+ $this->assertWidgetMatchesXpath($form->createView(), ['attr' => ['class' => 'my&class']],
+ '/div
+ [@class="my&class"]
+ [
+ ./input
+ [@id="name_year"]
+ [@type="number"]
+ [@class="form-control"]
+ [@value="2000"]
+ /following-sibling::input
+ [@id="name_week"]
+ [@type="number"]
+ [@class="form-control"]
+ [@value="1"]
+ ]
+ [count(./input)=2]
'
);
}
diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/week_widget.html.php b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/week_widget.html.php
new file mode 100644
index 0000000000..610b6e0c19
--- /dev/null
+++ b/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/week_widget.html.php
@@ -0,0 +1,14 @@
+
+ block($form, 'form_widget_simple'); ?>
+
+ ['size' => 1]] : [] ?>
+ block($form, 'widget_container_attributes') ?>>
+ widget($form['year'], $vars);
+ echo '-';
+ echo $view['form']->widget($form['week'], $vars);
+ ?>
+
+
diff --git a/src/Symfony/Component/Form/CHANGELOG.md b/src/Symfony/Component/Form/CHANGELOG.md
index ba3fb1d226..99e7078f0f 100644
--- a/src/Symfony/Component/Form/CHANGELOG.md
+++ b/src/Symfony/Component/Form/CHANGELOG.md
@@ -4,6 +4,7 @@ CHANGELOG
4.4.0
-----
+ * add new `WeekType`
* using different values for the "model_timezone" and "view_timezone" options of the `TimeType` without configuring a
reference date is deprecated
* preferred choices are repeated in the list of all choices
diff --git a/src/Symfony/Component/Form/Extension/Core/CoreExtension.php b/src/Symfony/Component/Form/Extension/Core/CoreExtension.php
index b658b9979f..1f3431cb49 100644
--- a/src/Symfony/Component/Form/Extension/Core/CoreExtension.php
+++ b/src/Symfony/Component/Form/Extension/Core/CoreExtension.php
@@ -83,6 +83,7 @@ class CoreExtension extends AbstractExtension
new Type\CurrencyType(),
new Type\TelType(),
new Type\ColorType(),
+ new Type\WeekType(),
];
}
diff --git a/src/Symfony/Component/Form/Extension/Core/DataTransformer/WeekToArrayTransformer.php b/src/Symfony/Component/Form/Extension/Core/DataTransformer/WeekToArrayTransformer.php
new file mode 100644
index 0000000000..51475e235c
--- /dev/null
+++ b/src/Symfony/Component/Form/Extension/Core/DataTransformer/WeekToArrayTransformer.php
@@ -0,0 +1,105 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Form\Extension\Core\DataTransformer;
+
+use Symfony\Component\Form\DataTransformerInterface;
+use Symfony\Component\Form\Exception\TransformationFailedException;
+
+/**
+ * Transforms between an ISO 8601 week date string and an array.
+ *
+ * @author Damien Fayet
+ */
+class WeekToArrayTransformer implements DataTransformerInterface
+{
+ /**
+ * Transforms a string containing an ISO 8601 week date into an array.
+ *
+ * @param string|null $value A week date string
+ *
+ * @return array A value containing year and week
+ *
+ * @throws TransformationFailedException If the given value is not a string,
+ * or if the given value does not follow the right format
+ */
+ public function transform($value)
+ {
+ if (null === $value) {
+ return ['year' => null, 'week' => null];
+ }
+
+ if (!\is_string($value)) {
+ throw new TransformationFailedException(sprintf('Value is expected to be a string but was "%s".', \is_object($value) ? \get_class($value) : \gettype($value)));
+ }
+
+ if (0 === preg_match('/^(?P\d{4})-W(?P\d{2})$/', $value, $matches)) {
+ throw new TransformationFailedException('Given data does not follow the date format "Y-\WW".');
+ }
+
+ return [
+ 'year' => (int) $matches['year'],
+ 'week' => (int) $matches['week'],
+ ];
+ }
+
+ /**
+ * Transforms an array into a week date string.
+ *
+ * @param array $value An array containing a year and a week number
+ *
+ * @return string|null A week date string following the format Y-\WW
+ *
+ * @throws TransformationFailedException If the given value can not be merged in a valid week date string,
+ * or if the obtained week date does not exists
+ */
+ public function reverseTransform($value)
+ {
+ if (null === $value || [] === $value) {
+ return null;
+ }
+
+ if (!\is_array($value)) {
+ throw new TransformationFailedException(sprintf('Value is expected to be an array, but was "%s".', \is_object($value) ? \get_class($value) : \gettype($value)));
+ }
+
+ if (!\array_key_exists('year', $value)) {
+ throw new TransformationFailedException('Key "year" is missing.');
+ }
+
+ if (!\array_key_exists('week', $value)) {
+ throw new TransformationFailedException('Key "week" is missing.');
+ }
+
+ if ($additionalKeys = array_diff(array_keys($value), ['year', 'week'])) {
+ throw new TransformationFailedException(sprintf('Expected only keys "year" and "week" to be present, but also got ["%s"].', implode('", "', $additionalKeys)));
+ }
+
+ if (null === $value['year'] && null === $value['week']) {
+ return null;
+ }
+
+ if (!\is_int($value['year'])) {
+ throw new TransformationFailedException(sprintf('Year is expected to be an integer, but was "%s".', \is_object($value['year']) ? \get_class($value['year']) : \gettype($value['year'])));
+ }
+
+ if (!\is_int($value['week'])) {
+ throw new TransformationFailedException(sprintf('Week is expected to be an integer, but was "%s".', \is_object($value['week']) ? \get_class($value['week']) : \gettype($value['week'])));
+ }
+
+ // The 28th December is always in the last week of the year
+ if (date('W', strtotime('28th December '.$value['year'])) < $value['week']) {
+ throw new TransformationFailedException(sprintf('Week "%d" does not exist for year "%d".', $value['week'], $value['year']));
+ }
+
+ return sprintf('%d-W%02d', $value['year'], $value['week']);
+ }
+}
diff --git a/src/Symfony/Component/Form/Extension/Core/Type/WeekType.php b/src/Symfony/Component/Form/Extension/Core/Type/WeekType.php
new file mode 100644
index 0000000000..70e0000ad9
--- /dev/null
+++ b/src/Symfony/Component/Form/Extension/Core/Type/WeekType.php
@@ -0,0 +1,192 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Form\Extension\Core\Type;
+
+use Symfony\Component\Form\AbstractType;
+use Symfony\Component\Form\Exception\LogicException;
+use Symfony\Component\Form\Extension\Core\DataTransformer\WeekToArrayTransformer;
+use Symfony\Component\Form\FormBuilderInterface;
+use Symfony\Component\Form\FormInterface;
+use Symfony\Component\Form\FormView;
+use Symfony\Component\Form\ReversedTransformer;
+use Symfony\Component\OptionsResolver\Options;
+use Symfony\Component\OptionsResolver\OptionsResolver;
+
+class WeekType extends AbstractType
+{
+ private static $widgets = [
+ 'text' => IntegerType::class,
+ 'choice' => ChoiceType::class,
+ ];
+
+ /**
+ * {@inheritdoc}
+ */
+ public function buildForm(FormBuilderInterface $builder, array $options)
+ {
+ if ('string' === $options['input']) {
+ $builder->addModelTransformer(new WeekToArrayTransformer());
+ }
+
+ if ('single_text' === $options['widget']) {
+ $builder->addViewTransformer(new ReversedTransformer(new WeekToArrayTransformer()));
+ } else {
+ $yearOptions = $weekOptions = [
+ 'error_bubbling' => true,
+ 'empty_data' => '',
+ ];
+ // when the form is compound the entries of the array are ignored in favor of children data
+ // so we need to handle the cascade setting here
+ $emptyData = $builder->getEmptyData() ?: [];
+
+ $yearOptions['empty_data'] = $emptyData['year'] ?? '';
+ $weekOptions['empty_data'] = $emptyData['week'] ?? '';
+
+ if (isset($options['invalid_message'])) {
+ $yearOptions['invalid_message'] = $options['invalid_message'];
+ $weekOptions['invalid_message'] = $options['invalid_message'];
+ }
+
+ if (isset($options['invalid_message_parameters'])) {
+ $yearOptions['invalid_message_parameters'] = $options['invalid_message_parameters'];
+ $weekOptions['invalid_message_parameters'] = $options['invalid_message_parameters'];
+ }
+
+ if ('choice' === $options['widget']) {
+ // Only pass a subset of the options to children
+ $yearOptions['choices'] = array_combine($options['years'], $options['years']);
+ $yearOptions['placeholder'] = $options['placeholder']['year'];
+ $yearOptions['choice_translation_domain'] = $options['choice_translation_domain']['year'];
+
+ $weekOptions['choices'] = array_combine($options['weeks'], $options['weeks']);
+ $weekOptions['placeholder'] = $options['placeholder']['week'];
+ $weekOptions['choice_translation_domain'] = $options['choice_translation_domain']['week'];
+
+ // Append generic carry-along options
+ foreach (['required', 'translation_domain'] as $passOpt) {
+ $yearOptions[$passOpt] = $options[$passOpt];
+ $weekOptions[$passOpt] = $options[$passOpt];
+ }
+ }
+
+ $builder->add('year', self::$widgets[$options['widget']], $yearOptions);
+ $builder->add('week', self::$widgets[$options['widget']], $weekOptions);
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function buildView(FormView $view, FormInterface $form, array $options)
+ {
+ $view->vars['widget'] = $options['widget'];
+
+ if ($options['html5']) {
+ $view->vars['type'] = 'week';
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function configureOptions(OptionsResolver $resolver)
+ {
+ $compound = function (Options $options) {
+ return 'single_text' !== $options['widget'];
+ };
+
+ $placeholderDefault = function (Options $options) {
+ return $options['required'] ? null : '';
+ };
+
+ $placeholderNormalizer = function (Options $options, $placeholder) use ($placeholderDefault) {
+ if (\is_array($placeholder)) {
+ $default = $placeholderDefault($options);
+
+ return array_merge(
+ ['year' => $default, 'week' => $default],
+ $placeholder
+ );
+ }
+
+ return [
+ 'year' => $placeholder,
+ 'week' => $placeholder,
+ ];
+ };
+
+ $choiceTranslationDomainNormalizer = function (Options $options, $choiceTranslationDomain) {
+ if (\is_array($choiceTranslationDomain)) {
+ $default = false;
+
+ return array_replace(
+ ['year' => $default, 'week' => $default],
+ $choiceTranslationDomain
+ );
+ }
+
+ return [
+ 'year' => $choiceTranslationDomain,
+ 'week' => $choiceTranslationDomain,
+ ];
+ };
+
+ $resolver->setDefaults([
+ 'years' => range(date('Y') - 10, date('Y') + 10),
+ 'weeks' => array_combine(range(1, 53), range(1, 53)),
+ 'widget' => 'single_text',
+ 'input' => 'array',
+ 'placeholder' => $placeholderDefault,
+ 'html5' => static function (Options $options) {
+ return 'single_text' === $options['widget'];
+ },
+ 'error_bubbling' => false,
+ 'empty_data' => function (Options $options) {
+ return $options['compound'] ? [] : '';
+ },
+ 'compound' => $compound,
+ 'choice_translation_domain' => false,
+ ]);
+
+ $resolver->setNormalizer('placeholder', $placeholderNormalizer);
+ $resolver->setNormalizer('choice_translation_domain', $choiceTranslationDomainNormalizer);
+ $resolver->setNormalizer('html5', function (Options $options, $html5) {
+ if ($html5 && 'single_text' !== $options['widget']) {
+ throw new LogicException(sprintf('The "widget" option of %s must be set to "single_text" when the "html5" option is enabled.', self::class));
+ }
+
+ return $html5;
+ });
+
+ $resolver->setAllowedValues('input', [
+ 'string',
+ 'array',
+ ]);
+
+ $resolver->setAllowedValues('widget', [
+ 'single_text',
+ 'text',
+ 'choice',
+ ]);
+
+ $resolver->setAllowedTypes('years', 'int[]');
+ $resolver->setAllowedTypes('weeks', 'int[]');
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getBlockPrefix()
+ {
+ return 'week';
+ }
+}
diff --git a/src/Symfony/Component/Form/Tests/AbstractLayoutTest.php b/src/Symfony/Component/Form/Tests/AbstractLayoutTest.php
index 8f1231e3bc..c565894ccc 100644
--- a/src/Symfony/Component/Form/Tests/AbstractLayoutTest.php
+++ b/src/Symfony/Component/Form/Tests/AbstractLayoutTest.php
@@ -2765,4 +2765,87 @@ abstract class AbstractLayoutTest extends FormIntegrationTestCase
[true],
];
}
+
+ public function testWeekSingleText()
+ {
+ $form = $this->factory->createNamed('holidays', 'Symfony\Component\Form\Extension\Core\Type\WeekType', '1970-W01', [
+ 'input' => 'string',
+ 'widget' => 'single_text',
+ ]);
+
+ $this->assertWidgetMatchesXpath($form->createView(), ['attr' => ['class' => 'my&class']],
+ '/input
+ [@type="week"]
+ [@name="holidays"]
+ [@class="my&class"]
+ [@value="1970-W01"]
+'
+ );
+ }
+
+ public function testWeekSingleTextNoHtml5()
+ {
+ $form = $this->factory->createNamed('holidays', 'Symfony\Component\Form\Extension\Core\Type\WeekType', '1970-W01', [
+ 'input' => 'string',
+ 'widget' => 'single_text',
+ 'html5' => false,
+ ]);
+
+ $this->assertWidgetMatchesXpath($form->createView(), ['attr' => ['class' => 'my&class']],
+ '/input
+ [@type="text"]
+ [@name="holidays"]
+ [@class="my&class"]
+ [@value="1970-W01"]
+'
+ );
+ }
+
+ public function testWeekChoices()
+ {
+ $data = ['year' => date('Y'), 'week' => 1];
+
+ $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\WeekType', $data, [
+ 'input' => 'array',
+ 'required' => false,
+ ]);
+
+ $this->assertWidgetMatchesXpath($form->createView(), ['attr' => ['class' => 'my&class']],
+ '/div
+ [@class="my&class"]
+ [
+ ./select
+ [@id="name_year"]
+ [./option[@value="'.$data['year'].'"][@selected="selected"]]
+ /following-sibling::select
+ [@id="name_week"]
+ [./option[@value="'.$data['week'].'"][@selected="selected"]]
+ ]
+ [count(.//select)=2]'
+ );
+ }
+
+ public function testWeekText()
+ {
+ $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\WeekType', '2000-W01', [
+ 'input' => 'string',
+ 'widget' => 'text',
+ ]);
+
+ $this->assertWidgetMatchesXpath($form->createView(), ['attr' => ['class' => 'my&class']],
+ '/div
+ [@class="my&class"]
+ [
+ ./input
+ [@id="name_year"]
+ [@type="number"]
+ [@value="2000"]
+ /following-sibling::input
+ [@id="name_week"]
+ [@type="number"]
+ [@value="1"]
+ ]
+ [count(./input)=2]'
+ );
+ }
}
diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/WeekToArrayTransformerTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/WeekToArrayTransformerTest.php
new file mode 100644
index 0000000000..3f681622a8
--- /dev/null
+++ b/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/WeekToArrayTransformerTest.php
@@ -0,0 +1,119 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Form\Tests\Extension\Core\DataTransformer;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\Form\Exception\TransformationFailedException;
+use Symfony\Component\Form\Extension\Core\DataTransformer\WeekToArrayTransformer;
+
+class WeekToArrayTransformerTest extends TestCase
+{
+ public function testTransform()
+ {
+ $transformer = new WeekToArrayTransformer();
+
+ $this->assertSame(['year' => 2019, 'week' => 1], $transformer->transform('2019-W01'));
+ }
+
+ public function testTransformEmpty()
+ {
+ $transformer = new WeekToArrayTransformer();
+
+ $this->assertSame(['year' => null, 'week' => null], $transformer->transform(null));
+ }
+
+ /**
+ * @dataProvider transformationFailuresProvider
+ */
+ public function testTransformationFailures($input, string $message)
+ {
+ $this->expectException(TransformationFailedException::class);
+ $this->expectExceptionMessage($message);
+
+ $transformer = new WeekToArrayTransformer();
+ $transformer->transform($input);
+ }
+
+ public function transformationFailuresProvider(): array
+ {
+ return [
+ 'malformed string' => ['lorem', 'Given data does not follow the date format "Y-\WW".'],
+ 'non-string' => [[], 'Value is expected to be a string but was "array".'],
+ ];
+ }
+
+ public function testReverseTransform()
+ {
+ $transformer = new WeekToArrayTransformer();
+
+ $input = [
+ 'year' => 2019,
+ 'week' => 1,
+ ];
+
+ $this->assertEquals('2019-W01', $transformer->reverseTransform($input));
+ }
+
+ public function testReverseTransformCompletelyEmpty()
+ {
+ $transformer = new WeekToArrayTransformer();
+
+ $input = [
+ 'year' => null,
+ 'week' => null,
+ ];
+
+ $this->assertNull($transformer->reverseTransform($input));
+ }
+
+ public function testReverseTransformNull()
+ {
+ $transformer = new WeekToArrayTransformer();
+
+ $this->assertNull($transformer->reverseTransform(null));
+ }
+
+ public function testReverseTransformEmpty()
+ {
+ $transformer = new WeekToArrayTransformer();
+
+ $this->assertNull($transformer->reverseTransform([]));
+ }
+
+ /**
+ * @dataProvider reverseTransformationFailuresProvider
+ */
+ public function testReverseTransformFailures($input, string $message)
+ {
+ $this->expectException(TransformationFailedException::class);
+ $this->expectExceptionMessage($message);
+
+ $transformer = new WeekToArrayTransformer();
+ $transformer->reverseTransform($input);
+ }
+
+ public function reverseTransformationFailuresProvider(): array
+ {
+ return [
+ 'missing year' => [['week' => 1], 'Key "year" is missing.'],
+ 'missing week' => [['year' => 2019], 'Key "week" is missing.'],
+ 'integer instead of array' => [0, 'Value is expected to be an array, but was "integer"'],
+ 'string instead of array' => ['12345', 'Value is expected to be an array, but was "string"'],
+ 'week invalid' => [['year' => 2019, 'week' => 66], 'Week "66" does not exist for year "2019".'],
+ 'year null' => [['year' => null, 'week' => 1], 'Year is expected to be an integer, but was "NULL".'],
+ 'week null' => [['year' => 2019, 'week' => null], 'Week is expected to be an integer, but was "NULL".'],
+ 'year non-integer' => [['year' => '2019', 'week' => 1], 'Year is expected to be an integer, but was "string".'],
+ 'week non-integer' => [['year' => 2019, 'week' => '1'], 'Week is expected to be an integer, but was "string".'],
+ 'unexpected key' => [['year' => 2019, 'bar' => 'baz', 'week' => 1, 'foo' => 'foobar'], 'Expected only keys "year" and "week" to be present, but also got ["bar", "foo"].'],
+ ];
+ }
+}
diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/Type/WeekTypeTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/Type/WeekTypeTest.php
new file mode 100644
index 0000000000..aea4c064b3
--- /dev/null
+++ b/src/Symfony/Component/Form/Tests/Extension/Core/Type/WeekTypeTest.php
@@ -0,0 +1,323 @@
+
+ *
+ * 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\FormError;
+
+class WeekTypeTest extends BaseTypeTest
+{
+ const TESTED_TYPE = 'Symfony\Component\Form\Extension\Core\Type\WeekType';
+
+ public function testSubmitArray()
+ {
+ $form = $this->factory->create(static::TESTED_TYPE, null, [
+ 'widget' => 'choice',
+ 'input' => 'array',
+ ]);
+
+ $form->submit([
+ 'year' => '2019',
+ 'week' => '1',
+ ]);
+
+ $this->assertSame(['year' => 2019, 'week' => 1], $form->getData());
+ }
+
+ public function testSubmitString()
+ {
+ $form = $this->factory->create(static::TESTED_TYPE, null, [
+ 'years' => [2019],
+ 'input' => 'string',
+ 'widget' => 'choice',
+ ]);
+
+ $form->submit([
+ 'year' => '2019',
+ 'week' => '1',
+ ]);
+
+ $this->assertEquals('2019-W01', $form->getData());
+ }
+
+ public function testSubmitStringSingleText()
+ {
+ $form = $this->factory->create(static::TESTED_TYPE, null, [
+ 'years' => [2019],
+ 'input' => 'string',
+ 'widget' => 'single_text',
+ ]);
+
+ $form->submit('2019-W01');
+
+ $this->assertEquals('2019-W01', $form->getData());
+ }
+
+ public function testPassDefaultPlaceholderToViewIfNotRequired()
+ {
+ $view = $this->factory->create(static::TESTED_TYPE, null, [
+ 'required' => false,
+ 'widget' => 'choice',
+ ])
+ ->createView();
+
+ $this->assertSame('', $view['year']->vars['placeholder']);
+ $this->assertSame('', $view['week']->vars['placeholder']);
+ }
+
+ public function testPassNoPlaceholderToViewIfRequired()
+ {
+ $view = $this->factory->create(static::TESTED_TYPE, null, [
+ 'required' => true,
+ 'widget' => 'choice',
+ ])
+ ->createView();
+
+ $this->assertNull($view['year']->vars['placeholder']);
+ $this->assertNull($view['week']->vars['placeholder']);
+ }
+
+ public function testPassPlaceholderAsString()
+ {
+ $view = $this->factory->create(static::TESTED_TYPE, null, [
+ 'placeholder' => 'Empty',
+ 'widget' => 'choice',
+ ])
+ ->createView();
+
+ $this->assertSame('Empty', $view['year']->vars['placeholder']);
+ $this->assertSame('Empty', $view['week']->vars['placeholder']);
+ }
+
+ public function testPassPlaceholderAsArray()
+ {
+ $view = $this->factory->create(static::TESTED_TYPE, null, [
+ 'placeholder' => [
+ 'year' => 'Empty year',
+ 'week' => 'Empty week',
+ ],
+ 'widget' => 'choice',
+ ])
+ ->createView();
+
+ $this->assertSame('Empty year', $view['year']->vars['placeholder']);
+ $this->assertSame('Empty week', $view['week']->vars['placeholder']);
+ }
+
+ public function testPassPlaceholderAsPartialArrayAddEmptyIfNotRequired()
+ {
+ $view = $this->factory->create(static::TESTED_TYPE, null, [
+ 'required' => false,
+ 'placeholder' => [
+ 'year' => 'Empty year',
+ ],
+ 'widget' => 'choice',
+ ])
+ ->createView();
+
+ $this->assertSame('Empty year', $view['year']->vars['placeholder']);
+ $this->assertSame('', $view['week']->vars['placeholder']);
+ }
+
+ public function testPassPlaceholderAsPartialArrayAddNullIfRequired()
+ {
+ $view = $this->factory->create(static::TESTED_TYPE, null, [
+ 'required' => true,
+ 'placeholder' => [
+ 'year' => 'Empty year',
+ ],
+ 'widget' => 'choice',
+ ])
+ ->createView();
+
+ $this->assertSame('Empty year', $view['year']->vars['placeholder']);
+ $this->assertNull($view['week']->vars['placeholder']);
+ }
+
+ public function testPassHtml5TypeIfSingleTextAndHtml5Format()
+ {
+ $view = $this->factory->create(static::TESTED_TYPE, null, [
+ 'widget' => 'single_text',
+ ])
+ ->createView();
+
+ $this->assertSame('week', $view->vars['type']);
+ }
+
+ public function testDontPassHtml5TypeIfHtml5NotAllowed()
+ {
+ $view = $this->factory->create(static::TESTED_TYPE, null, [
+ 'widget' => 'single_text',
+ 'html5' => false,
+ ])
+ ->createView();
+
+ $this->assertArrayNotHasKey('type', $view->vars);
+ }
+
+ public function testDontPassHtml5TypeIfNotSingleText()
+ {
+ $view = $this->factory->create(static::TESTED_TYPE, null, [
+ 'widget' => 'text',
+ ])
+ ->createView();
+
+ $this->assertArrayNotHasKey('type', $view->vars);
+ }
+
+ public function testYearTypeChoiceErrorsBubbleUp()
+ {
+ $error = new FormError('Invalid!');
+ $form = $this->factory->create(static::TESTED_TYPE, null, [
+ 'widget' => 'choice',
+ ]);
+
+ $form['year']->addError($error);
+
+ $this->assertSame([], iterator_to_array($form['year']->getErrors()));
+ $this->assertSame([$error], iterator_to_array($form->getErrors()));
+ }
+
+ public function testWeekTypeChoiceErrorsBubbleUp()
+ {
+ $error = new FormError('Invalid!');
+ $form = $this->factory->create(static::TESTED_TYPE, null, [
+ 'widget' => 'choice',
+ ]);
+
+ $form['week']->addError($error);
+
+ $this->assertSame([], iterator_to_array($form['week']->getErrors()));
+ $this->assertSame([$error], iterator_to_array($form->getErrors()));
+ }
+
+ public function testPassDefaultChoiceTranslationDomain()
+ {
+ $form = $this->factory->create(static::TESTED_TYPE, null, [
+ 'widget' => 'choice',
+ ]);
+
+ $view = $form->createView();
+
+ $this->assertFalse($view['year']->vars['choice_translation_domain']);
+ $this->assertFalse($view['week']->vars['choice_translation_domain']);
+ }
+
+ public function testPassChoiceTranslationDomainAsString()
+ {
+ $form = $this->factory->create(static::TESTED_TYPE, null, [
+ 'choice_translation_domain' => 'messages',
+ 'widget' => 'choice',
+ ]);
+
+ $view = $form->createView();
+ $this->assertSame('messages', $view['year']->vars['choice_translation_domain']);
+ $this->assertSame('messages', $view['week']->vars['choice_translation_domain']);
+ }
+
+ public function testPassChoiceTranslationDomainAsArray()
+ {
+ $form = $this->factory->create(static::TESTED_TYPE, null, [
+ 'choice_translation_domain' => [
+ 'year' => 'foo',
+ 'week' => 'test',
+ ],
+ 'widget' => 'choice',
+ ]);
+
+ $view = $form->createView();
+ $this->assertSame('foo', $view['year']->vars['choice_translation_domain']);
+ $this->assertSame('test', $view['week']->vars['choice_translation_domain']);
+ }
+
+ public function testSubmitNull($expected = null, $norm = null, $view = null)
+ {
+ $form = $this->factory->create($this->getTestedType(), null, [
+ 'widget' => 'choice',
+ ]);
+ $form->submit(null);
+
+ $this->assertSame(['year' => null, 'week' => null], $form->getData());
+ $this->assertSame(['year' => null, 'week' => null], $form->getNormData());
+ $this->assertSame(['year' => null, 'week' => null], $form->getViewData());
+ }
+
+ public function testSubmitFromChoiceEmpty()
+ {
+ $form = $this->factory->create(static::TESTED_TYPE, null, [
+ 'widget' => 'choice',
+ 'required' => false,
+ ]);
+
+ $form->submit([
+ 'year' => '',
+ 'week' => '',
+ ]);
+
+ $this->assertSame(['year' => null, 'week' => null], $form->getData());
+ }
+
+ public function testSubmitNullWithText()
+ {
+ $form = $this->factory->create(static::TESTED_TYPE, null, [
+ 'widget' => 'text',
+ ]);
+ $form->submit(null);
+
+ $this->assertSame(['year' => null, 'week' => null], $form->getViewData());
+ }
+
+ public function testSubmitNullWithSingleText()
+ {
+ $form = $this->factory->create(static::TESTED_TYPE, null, [
+ 'widget' => 'single_text',
+ 'input' => 'string',
+ ]);
+ $form->submit(null);
+
+ $this->assertNull($form->getData());
+ $this->assertNull($form->getNormData());
+ $this->assertSame('', $form->getViewData());
+ }
+
+ public function testSubmitNullUsesDefaultEmptyData($emptyData = [], $expectedData = null)
+ {
+ $form = $this->factory->create(static::TESTED_TYPE, null, [
+ 'empty_data' => $emptyData,
+ 'widget' => 'choice',
+ ]);
+ $form->submit(null);
+
+ $this->assertSame(['year' => null, 'week' => null], $form->getData());
+ }
+
+ /**
+ * @dataProvider provideEmptyData
+ */
+ public function testSubmitNullUsesDateEmptyDataString($widget, $emptyData, $expectedData)
+ {
+ $form = $this->factory->create(static::TESTED_TYPE, null, [
+ 'widget' => $widget,
+ 'empty_data' => $emptyData,
+ ]);
+ $form->submit(null);
+
+ $this->assertSame($expectedData, $form->getData());
+ }
+
+ public function provideEmptyData()
+ {
+ return [
+ 'Compound text field' => ['text', ['year' => '2019', 'week' => '1'], ['year' => 2019, 'week' => 1]],
+ 'Compound choice field' => ['choice', ['year' => '2019', 'week' => '1'], ['year' => 2019, 'week' => 1]],
+ ];
+ }
+}