Skip to content

Commit a97942d

Browse files
committed
Added a "html5" option to PercentType, to render an input[type="number"]. Added tests over PercentType.
1 parent 75e71e3 commit a97942d

File tree

8 files changed

+190
-9
lines changed

8 files changed

+190
-9
lines changed

src/Symfony/Component/Form/CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ CHANGELOG
2121
* Deprecated `Symfony\Component\Form\Extension\Validator\Util\ServerParams` in favor of its parent class `Symfony\Component\Form\Util\ServerParams`
2222
* Added the `html5` option to the `ColorType` to validate the input
2323
* Deprecated `NumberToLocalizedStringTransformer::ROUND_*` constants, use `\NumberFormatter::ROUND_*` instead
24+
* Added a `html5` option to `MoneyType` and `PercentType`, to use `<input type="number" />`
25+
2426

2527
5.0.0
2628
-----

src/Symfony/Component/Form/Extension/Core/DataTransformer/MoneyToLocalizedStringTransformer.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ class MoneyToLocalizedStringTransformer extends NumberToLocalizedStringTransform
2323
{
2424
private $divisor;
2525

26-
public function __construct(?int $scale = 2, ?bool $grouping = true, ?int $roundingMode = \NumberFormatter::ROUND_HALFUP, ?int $divisor = 1)
26+
public function __construct(?int $scale = 2, ?bool $grouping = true, ?int $roundingMode = \NumberFormatter::ROUND_HALFUP, ?int $divisor = 1, ?string $locale = null)
2727
{
2828
if (null === $grouping) {
2929
$grouping = true;
@@ -33,7 +33,7 @@ public function __construct(?int $scale = 2, ?bool $grouping = true, ?int $round
3333
$scale = 2;
3434
}
3535

36-
parent::__construct($scale, $grouping, $roundingMode);
36+
parent::__construct($scale, $grouping, $roundingMode, $locale);
3737

3838
if (null === $divisor) {
3939
$divisor = 1;

src/Symfony/Component/Form/Extension/Core/DataTransformer/PercentToLocalizedStringTransformer.php

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,16 +34,19 @@ class PercentToLocalizedStringTransformer implements DataTransformerInterface
3434
private $roundingMode;
3535
private $type;
3636
private $scale;
37+
private $html5Format;
3738

3839
/**
3940
* @see self::$types for a list of supported types
4041
*
41-
* @param int $scale The scale
42-
* @param string $type One of the supported types
42+
* @param int $scale The scale
43+
* @param string $type One of the supported types
44+
* @param int|null $roundingMode a value from \NumberFormatter, such as \NumberFormatter::ROUND_HALFUP
45+
* @param bool $html5Format Use a HTML5 specific format, as per https://www.w3.org/TR/html51/sec-forms.html#date-time-and-number-formats
4346
*
4447
* @throws UnexpectedTypeException if the given value of type is unknown
4548
*/
46-
public function __construct(int $scale = null, string $type = null, ?int $roundingMode = null)
49+
public function __construct(int $scale = null, string $type = null, ?int $roundingMode = null, bool $html5Format = false)
4750
{
4851
if (null === $scale) {
4952
$scale = 0;
@@ -64,6 +67,7 @@ public function __construct(int $scale = null, string $type = null, ?int $roundi
6467
$this->type = $type;
6568
$this->scale = $scale;
6669
$this->roundingMode = $roundingMode;
70+
$this->html5Format = $html5Format;
6771
}
6872

6973
/**
@@ -182,7 +186,13 @@ public function reverseTransform($value)
182186
*/
183187
protected function getNumberFormatter()
184188
{
185-
$formatter = new \NumberFormatter(\Locale::getDefault(), \NumberFormatter::DECIMAL);
189+
// Values used in HTML5 number inputs should be formatted as in "1234.5", ie. 'en' format without grouping,
190+
// according to https://www.w3.org/TR/html51/sec-forms.html#date-time-and-number-formats
191+
$formatter = new \NumberFormatter($this->html5Format ? 'en' : \Locale::getDefault(), \NumberFormatter::DECIMAL);
192+
193+
if ($this->html5Format) {
194+
$formatter->setAttribute(\NumberFormatter::GROUPING_USED, 0);
195+
}
186196

187197
$formatter->setAttribute(\NumberFormatter::FRACTION_DIGITS, $this->scale);
188198

src/Symfony/Component/Form/Extension/Core/Type/MoneyType.php

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,12 @@
1212
namespace Symfony\Component\Form\Extension\Core\Type;
1313

1414
use Symfony\Component\Form\AbstractType;
15+
use Symfony\Component\Form\Exception\LogicException;
1516
use Symfony\Component\Form\Extension\Core\DataTransformer\MoneyToLocalizedStringTransformer;
1617
use Symfony\Component\Form\FormBuilderInterface;
1718
use Symfony\Component\Form\FormInterface;
1819
use Symfony\Component\Form\FormView;
20+
use Symfony\Component\OptionsResolver\Options;
1921
use Symfony\Component\OptionsResolver\OptionsResolver;
2022

2123
class MoneyType extends AbstractType
@@ -27,12 +29,15 @@ class MoneyType extends AbstractType
2729
*/
2830
public function buildForm(FormBuilderInterface $builder, array $options)
2931
{
32+
// Values used in HTML5 number inputs should be formatted as in "1234.5", ie. 'en' format without grouping,
33+
// according to https://www.w3.org/TR/html51/sec-forms.html#date-time-and-number-formats
3034
$builder
3135
->addViewTransformer(new MoneyToLocalizedStringTransformer(
3236
$options['scale'],
33-
$options['grouping'],
37+
$options['html5'] ? $options['grouping'] : false,
3438
$options['rounding_mode'],
35-
$options['divisor']
39+
$options['divisor'],
40+
$options['html5'] ? 'en' : null
3641
))
3742
;
3843
}
@@ -43,6 +48,10 @@ public function buildForm(FormBuilderInterface $builder, array $options)
4348
public function buildView(FormView $view, FormInterface $form, array $options)
4449
{
4550
$view->vars['money_pattern'] = self::getPattern($options['currency']);
51+
52+
if ($options['html5']) {
53+
$view->vars['type'] = 'number';
54+
}
4655
}
4756

4857
/**
@@ -57,6 +66,7 @@ public function configureOptions(OptionsResolver $resolver)
5766
'divisor' => 1,
5867
'currency' => 'EUR',
5968
'compound' => false,
69+
'html5' => false,
6070
]);
6171

6272
$resolver->setAllowedValues('rounding_mode', [
@@ -70,6 +80,16 @@ public function configureOptions(OptionsResolver $resolver)
7080
]);
7181

7282
$resolver->setAllowedTypes('scale', 'int');
83+
84+
$resolver->setAllowedTypes('html5', 'bool');
85+
86+
$resolver->setNormalizer('grouping', function (Options $options, $value) {
87+
if (true === $value && $options['html5']) {
88+
throw new LogicException('Cannot use the "grouping" option when the "html5" option is enabled.');
89+
}
90+
91+
return $value;
92+
});
7393
}
7494

7595
/**

src/Symfony/Component/Form/Extension/Core/Type/PercentType.php

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ public function buildForm(FormBuilderInterface $builder, array $options)
3030
$options['scale'],
3131
$options['type'],
3232
$options['rounding_mode'],
33-
false
33+
$options['html5']
3434
));
3535
}
3636

@@ -40,6 +40,10 @@ public function buildForm(FormBuilderInterface $builder, array $options)
4040
public function buildView(FormView $view, FormInterface $form, array $options)
4141
{
4242
$view->vars['symbol'] = $options['symbol'];
43+
44+
if ($options['html5']) {
45+
$view->vars['type'] = 'number';
46+
}
4347
}
4448

4549
/**
@@ -57,6 +61,7 @@ public function configureOptions(OptionsResolver $resolver)
5761
'symbol' => '%',
5862
'type' => 'fractional',
5963
'compound' => false,
64+
'html5' => false,
6065
]);
6166

6267
$resolver->setAllowedValues('type', [
@@ -82,6 +87,7 @@ public function configureOptions(OptionsResolver $resolver)
8287

8388
return '';
8489
});
90+
$resolver->setAllowedTypes('html5', 'bool');
8591
}
8692

8793
/**

src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/PercentToLocalizedStringTransformerTest.php

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -412,4 +412,84 @@ public function testReverseTransformDisallowsTrailingExtraCharactersMultibyte()
412412

413413
$transformer->reverseTransform("12\xc2\xa0345,678foo");
414414
}
415+
416+
public function testTransformForHtml5Format()
417+
{
418+
$transformer = new PercentToLocalizedStringTransformer(null, null, \NumberFormatter::ROUND_HALFUP, true);
419+
420+
// Since we test against "de_CH", we need the full implementation
421+
IntlTestHelper::requireFullIntl($this, false);
422+
423+
\Locale::setDefault('de_CH');
424+
425+
$this->assertEquals('10', $transformer->transform(0.104));
426+
$this->assertEquals('11', $transformer->transform(0.105));
427+
$this->assertEquals('200000', $transformer->transform(2000));
428+
}
429+
430+
public function testTransformForHtml5FormatWithInteger()
431+
{
432+
$transformer = new PercentToLocalizedStringTransformer(null, 'integer', \NumberFormatter::ROUND_HALFUP, true);
433+
434+
// Since we test against "de_CH", we need the full implementation
435+
IntlTestHelper::requireFullIntl($this, false);
436+
437+
\Locale::setDefault('de_CH');
438+
439+
$this->assertEquals('0', $transformer->transform(0.1));
440+
$this->assertEquals('1234', $transformer->transform(1234));
441+
}
442+
443+
public function testTransformForHtml5FormatWithScale()
444+
{
445+
// Since we test against "de_CH", we need the full implementation
446+
IntlTestHelper::requireFullIntl($this, false);
447+
448+
\Locale::setDefault('de_CH');
449+
450+
$transformer = new PercentToLocalizedStringTransformer(2, null, \NumberFormatter::ROUND_HALFUP, true);
451+
452+
$this->assertEquals('12.34', $transformer->transform(0.1234));
453+
}
454+
455+
public function testReverseTransformForHtml5Format()
456+
{
457+
// Since we test against "de_CH", we need the full implementation
458+
IntlTestHelper::requireFullIntl($this, false);
459+
460+
\Locale::setDefault('de_CH');
461+
462+
$transformer = new PercentToLocalizedStringTransformer(null, null, \NumberFormatter::ROUND_HALFUP, true);
463+
464+
$this->assertEquals(0.02, $transformer->reverseTransform('1.5')); // rounded up, for 2 decimals
465+
$this->assertEquals(0.15, $transformer->reverseTransform('15'));
466+
$this->assertEquals(2000, $transformer->reverseTransform('200000'));
467+
}
468+
469+
public function testReverseTransformForHtml5FormatWithInteger()
470+
{
471+
// Since we test against "de_CH", we need the full implementation
472+
IntlTestHelper::requireFullIntl($this, false);
473+
474+
\Locale::setDefault('de_CH');
475+
476+
$transformer = new PercentToLocalizedStringTransformer(null, 'integer', \NumberFormatter::ROUND_HALFUP, true);
477+
478+
$this->assertEquals(10, $transformer->reverseTransform('10'));
479+
$this->assertEquals(15, $transformer->reverseTransform('15'));
480+
$this->assertEquals(12, $transformer->reverseTransform('12'));
481+
$this->assertEquals(200, $transformer->reverseTransform('200'));
482+
}
483+
484+
public function testReverseTransformForHtml5FormatWithScale()
485+
{
486+
// Since we test against "de_CH", we need the full implementation
487+
IntlTestHelper::requireFullIntl($this, false);
488+
489+
\Locale::setDefault('de_CH');
490+
491+
$transformer = new PercentToLocalizedStringTransformer(2, null, \NumberFormatter::ROUND_HALFUP, true);
492+
493+
$this->assertEquals(0.1234, $transformer->reverseTransform('12.34'));
494+
}
415495
}

src/Symfony/Component/Form/Tests/Extension/Core/Type/MoneyTypeTest.php

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,4 +109,18 @@ public function testDefaultFormattingWithSpecifiedRounding()
109109

110110
$this->assertSame('12345', $form->createView()->vars['value']);
111111
}
112+
113+
public function testHtml5EnablesSpecificFormatting()
114+
{
115+
// Since we test against "de_CH", we need the full implementation
116+
IntlTestHelper::requireFullIntl($this, false);
117+
118+
\Locale::setDefault('de_CH');
119+
120+
$form = $this->factory->create(static::TESTED_TYPE, null, ['html5' => true, 'scale' => 2]);
121+
$form->setData('12345.6');
122+
123+
$this->assertSame('12345.60', $form->createView()->vars['value']);
124+
$this->assertSame('number', $form->createView()->vars['type']);
125+
}
112126
}

src/Symfony/Component/Form/Tests/Extension/Core/Type/PercentTypeTest.php

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,34 @@
1414
use Symfony\Bridge\PhpUnit\ExpectDeprecationTrait;
1515
use Symfony\Component\Form\Extension\Core\Type\PercentType;
1616
use Symfony\Component\Form\Test\TypeTestCase;
17+
use Symfony\Component\Intl\Util\IntlTestHelper;
1718

1819
class PercentTypeTest extends TypeTestCase
1920
{
2021
use ExpectDeprecationTrait;
2122

2223
const TESTED_TYPE = PercentType::class;
2324

25+
private $defaultLocale;
26+
27+
protected function setUp(): void
28+
{
29+
// we test against different locales, so we need the full
30+
// implementation
31+
IntlTestHelper::requireFullIntl($this, false);
32+
33+
parent::setUp();
34+
35+
$this->defaultLocale = \Locale::getDefault();
36+
}
37+
38+
protected function tearDown(): void
39+
{
40+
parent::tearDown();
41+
42+
\Locale::setDefault($this->defaultLocale);
43+
}
44+
2445
public function testSubmitWithRoundingMode()
2546
{
2647
$form = $this->factory->create(self::TESTED_TYPE, null, [
@@ -33,6 +54,34 @@ public function testSubmitWithRoundingMode()
3354
$this->assertEquals(0.0124, $form->getData());
3455
}
3556

57+
public function testSubmitNullUsesDefaultEmptyData($emptyData = '10', $expectedData = 0.1)
58+
{
59+
$form = $this->factory->create(static::TESTED_TYPE, null, [
60+
'empty_data' => $emptyData,
61+
]);
62+
$form->submit(null);
63+
64+
$this->assertSame($emptyData, $form->getViewData());
65+
$this->assertSame($expectedData, $form->getNormData());
66+
$this->assertSame($expectedData, $form->getData());
67+
}
68+
69+
public function testHtml5EnablesSpecificFormatting()
70+
{
71+
\Locale::setDefault('de_CH');
72+
73+
$form = $this->factory->create(static::TESTED_TYPE, null, [
74+
'html5' => true,
75+
'rounding_mode' => \NumberFormatter::ROUND_UP,
76+
'scale' => 2,
77+
'type' => 'integer',
78+
]);
79+
$form->setData('1234.56');
80+
81+
$this->assertSame('1234.56', $form->createView()->vars['value']);
82+
$this->assertSame('number', $form->createView()->vars['type']);
83+
}
84+
3685
/**
3786
* @group legacy
3887
*/

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy