Skip to content

Commit cf449e8

Browse files
committed
feature #61837 [Form] Add new active_at, not_active_at and legal_tender options to CurrencyType (Crovitche-1623)
This PR was squashed before being merged into the 7.4 branch. Discussion ---------- [Form] Add new `active_at`, `not_active_at` and `legal_tender` options to `CurrencyType` | Q | A | ------------- | --- | Branch? | 7.4 | Bug fix? | no | New feature? | yes | Deprecations? | no | Issues | - | License | MIT Add the options `active_at`, `not_active_at` and `legal_tender` from #61431 that give the possibility to filter the currencies more precisely. For instance: - Keep only the current ones for the given `active_at` (`today` by default) - Keep only the currencies that are [legal tender](https://en.wikipedia.org/wiki/Legal_tender) Regarding BC compatibility, if people want to have the same results as prior 7.4, they should set the `active_at` and `legal_tender` options to null explicitely. Commits ------- a378fe6c58a [Form] Add new `active_at`, `not_active_at` and `legal_tender` options to `CurrencyType`
2 parents 6cc6ccd + d304325 commit cf449e8

File tree

3 files changed

+110
-3
lines changed

3 files changed

+110
-3
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ CHANGELOG
66

77
* Add `input=date_point` to `DateTimeType`, `DateType` and `TimeType`
88
* Add support for guessing form type of enum properties
9+
* Add `active_at`, `not_active_at` and `legal_tender` options to `CurrencyType`
910

1011
7.3
1112
---

Extension/Core/Type/CurrencyType.php

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
use Symfony\Component\Form\Exception\LogicException;
1818
use Symfony\Component\Intl\Currencies;
1919
use Symfony\Component\Intl\Intl;
20+
use Symfony\Component\OptionsResolver\Exception\InvalidOptionsException;
2021
use Symfony\Component\OptionsResolver\Options;
2122
use Symfony\Component\OptionsResolver\OptionsResolver;
2223

@@ -31,15 +32,62 @@ public function configureOptions(OptionsResolver $resolver): void
3132
}
3233

3334
$choiceTranslationLocale = $options['choice_translation_locale'];
35+
$activeAt = $options['active_at'];
36+
$notActiveAt = $options['not_active_at'];
37+
$legalTender = $options['legal_tender'];
3438

35-
return ChoiceList::loader($this, new IntlCallbackChoiceLoader(static fn () => array_flip(Currencies::getNames($choiceTranslationLocale))), $choiceTranslationLocale);
39+
if (null !== $activeAt && null !== $notActiveAt) {
40+
throw new InvalidOptionsException('The "active_at" and "not_active_at" options cannot be used together.');
41+
}
42+
43+
$legalTenderCacheKey = match ($legalTender) {
44+
null => '',
45+
true => '1',
46+
false => '0',
47+
};
48+
49+
return ChoiceList::loader(
50+
$this,
51+
new IntlCallbackChoiceLoader(
52+
static function () use ($choiceTranslationLocale, $activeAt, $notActiveAt, $legalTender) {
53+
if (null === $activeAt && null === $notActiveAt && null === $legalTender) {
54+
return array_flip(Currencies::getNames($choiceTranslationLocale));
55+
}
56+
57+
$filteredCurrencyNames = [];
58+
59+
$active = match (true) {
60+
null !== $activeAt => true,
61+
null !== $notActiveAt => false,
62+
default => null,
63+
};
64+
65+
foreach (Currencies::getCurrencyCodes() as $code) {
66+
if (!Currencies::isValidInAnyCountry($code, $legalTender, $active, $activeAt ?? $notActiveAt)) {
67+
continue;
68+
}
69+
70+
$filteredCurrencyNames[$code] = Currencies::getName($code, $choiceTranslationLocale);
71+
}
72+
73+
return array_flip($filteredCurrencyNames);
74+
},
75+
),
76+
$choiceTranslationLocale.($activeAt ?? $notActiveAt)?->format('Y-m-d\TH:i:s').$legalTenderCacheKey,
77+
);
3678
},
3779
'choice_translation_domain' => false,
3880
'choice_translation_locale' => null,
81+
'active_at' => new \DateTimeImmutable('today', new \DateTimeZone('Etc/UTC')),
82+
'not_active_at' => null,
83+
'legal_tender' => true,
3984
'invalid_message' => 'Please select a valid currency.',
4085
]);
4186

4287
$resolver->setAllowedTypes('choice_translation_locale', ['null', 'string']);
88+
$resolver->setAllowedTypes('active_at', [\DateTimeInterface::class, 'null']);
89+
$resolver->setAllowedTypes('not_active_at', [\DateTimeInterface::class, 'null']);
90+
$resolver->setAllowedTypes('legal_tender', ['bool', 'null']);
4391
}
4492

4593
public function getParent(): ?string

Tests/Extension/Core/Type/CurrencyTypeTest.php

Lines changed: 60 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
use Symfony\Component\Form\ChoiceList\View\ChoiceView;
1616
use Symfony\Component\Form\Extension\Core\Type\CurrencyType;
1717
use Symfony\Component\Intl\Util\IntlTestHelper;
18+
use Symfony\Component\OptionsResolver\Exception\InvalidOptionsException;
1819

1920
class CurrencyTypeTest extends BaseTypeTestCase
2021
{
@@ -34,7 +35,6 @@ public function testCurrenciesAreSelectable()
3435

3536
$this->assertContainsEquals(new ChoiceView('EUR', 'EUR', 'Euro'), $choices);
3637
$this->assertContainsEquals(new ChoiceView('USD', 'USD', 'US Dollar'), $choices);
37-
$this->assertContainsEquals(new ChoiceView('SIT', 'SIT', 'Slovenian Tolar'), $choices);
3838
}
3939

4040
#[RequiresPhpExtension('intl')]
@@ -49,7 +49,6 @@ public function testChoiceTranslationLocaleOption()
4949
// Don't check objects for identity
5050
$this->assertContainsEquals(new ChoiceView('EUR', 'EUR', 'євро'), $choices);
5151
$this->assertContainsEquals(new ChoiceView('USD', 'USD', 'долар США'), $choices);
52-
$this->assertContainsEquals(new ChoiceView('SIT', 'SIT', 'словенський толар'), $choices);
5352
}
5453

5554
public function testSubmitNull($expected = null, $norm = null, $view = null)
@@ -61,4 +60,63 @@ public function testSubmitNullUsesDefaultEmptyData($emptyData = 'EUR', $expected
6160
{
6261
parent::testSubmitNullUsesDefaultEmptyData($emptyData, $expectedData);
6362
}
63+
64+
#[RequiresPhpExtension('intl')]
65+
public function testAnActiveAndLegalTenderCurrencyIn2006()
66+
{
67+
$choices = $this->factory
68+
->create(static::TESTED_TYPE, null, [
69+
'choice_translation_locale' => 'fr',
70+
'active_at' => new \DateTimeImmutable('2006-01-01', new \DateTimeZone('Etc/UTC')),
71+
'legal_tender' => true,
72+
])
73+
->createView()->vars['choices'];
74+
75+
$this->assertContainsEquals(new ChoiceView('SIT', 'SIT', 'tolar slovène'), $choices);
76+
}
77+
78+
#[RequiresPhpExtension('intl')]
79+
public function testAnExpiredCurrencyIn2007()
80+
{
81+
$choices = $this->factory
82+
->create(static::TESTED_TYPE, null, [
83+
'choice_translation_locale' => 'fr',
84+
'legal_tender' => true,
85+
// The SIT currency expired on 2007-01-14.
86+
'active_at' => new \DateTimeImmutable('2007-01-15', new \DateTimeZone('Etc/UTC')),
87+
])
88+
->createView()->vars['choices'];
89+
90+
$this->assertNotContainsEquals(new ChoiceView('SIT', 'SIT', 'tolar slovène'), $choices);
91+
}
92+
93+
#[RequiresPhpExtension('intl')]
94+
public function testRetrieveExpiredCurrenciesIn2007()
95+
{
96+
$choices = $this->factory
97+
->create(static::TESTED_TYPE, null, [
98+
'choice_translation_locale' => 'fr',
99+
'legal_tender' => true,
100+
'active_at' => null,
101+
// The SIT currency expired on 2007-01-14.
102+
'not_active_at' => new \DateTimeImmutable('2007-01-15', new \DateTimeZone('Etc/UTC')),
103+
])
104+
->createView()->vars['choices'];
105+
106+
$this->assertContainsEquals(new ChoiceView('SIT', 'SIT', 'tolar slovène'), $choices);
107+
}
108+
109+
public function testAnExceptionShouldBeThrownWhenTheActiveAtAndNotActiveAtOptionsAreBothSet()
110+
{
111+
$this->expectException(InvalidOptionsException::class);
112+
113+
$this->expectExceptionMessage('The "active_at" and "not_active_at" options cannot be used together.');
114+
115+
$this->factory
116+
->create(static::TESTED_TYPE, null, [
117+
'active_at' => new \DateTimeImmutable(),
118+
'not_active_at' => new \DateTimeImmutable(),
119+
])
120+
->createView();
121+
}
64122
}

0 commit comments

Comments
 (0)