1: <?php
2: namespace TIC\CoreBundle\Form\DataTransformer;
3:
4: use Symfony\Component\Form\Extension\Core\DataTransformer\NumberToLocalizedStringTransformer;
5:
6: /**
7: * Transforms between a number type and a localized number
8: * with grouping (each thousand) and comma separators.
9: */
10: class NumberToStringTransformer extends NumberToLocalizedStringTransformer
11: {
12: protected $groupingSep; /** @var Caractère(s) utilisé(s) comme séparateur de milliers */
13: protected $decimalSep; /** @var Caractère(s) utilisé(s) comme séparateur décimal */
14: protected $multiplier; /** @var Coefficient multiplicateur à appliquer avant l'affichage */
15: protected $locale; /** @var Locale utilisée pour le formattage de la valeur numérique */
16: protected $fixed; /** @var Pour que les décimales soient toujours présentes (même nulle) */
17: protected $precision; /** @var Doublon de la propriété 'scale' du parent qui est privée */
18:
19: /**
20: * Construct the data transformer with parameters.
21: *
22: * @param int $scale Précision (nombre de décimales)
23: * @param bool $grouping Formattage en utilisant le séparateur de milliers
24: * @param int $roundingMode Constante définissant la méthode d'arrondi
25: * @param mixed $multiplier Coefficient numérique à appliquer à la valeur
26: * @param string $locale Locale à utiliser pour le formattage des nombres
27: * @param bool $fixed Force l'affichage de toutes les décimales (0 padding)
28: */
29: public function __construct(int $scale = null, ?bool $grouping = false, ?int $roundingMode = \NumberFormatter::ROUND_HALFUP, mixed $multiplier = null, string $locale = null, bool $fixed = false)
30: {
31: parent::__construct($scale, $grouping, $roundingMode);
32: $this->precision = $scale;
33:
34: // le paramètre $grouping peut aussi servir à spécifier le séparateur à utiliser
35: if (! \is_bool($this->grouping)) {
36: $this->groupingSep = $this->grouping;
37: $this->grouping = true;
38: }
39:
40: // coefficient multiplicateur à appliquer sur la valeur à afficher
41: if (! empty($multiplier) && \is_numeric($multiplier)) $this->multiplier = $multiplier;
42:
43: // détermination de la locale à utiliser (null|true automatique ; false 'en')
44: if ($locale === null || $locale === true) $this->locale = \Locale::getDefault();
45: elseif ($locale === false) $this->locale = 'en';
46: else $this->locale = $locale;
47:
48: $this->fixed = $fixed;
49: }
50:
51: /**
52: * Transforms a number type into localized number.
53: *
54: * @param int|float $value The numeric value
55: * @return string The localized value
56: * @throws TransformationFailedException
57: */
58: public function transform(mixed $value): string
59: {
60:
61: // application du coefficient multiplicateur
62: if (($value !== null) && \is_numeric($value) && ($this->multiplier !== null)) $value *= $this->multiplier;
63:
64: return parent::transform($value);
65: }
66:
67: /**
68: * Transforms a localized number into an integer or float.
69: *
70: * @param string $value The localized value
71: * @return int|float The numeric value
72: * @throws TransformationFailedException
73: */
74: public function reverseTransform(mixed $value): int|float|null
75: {
76: $result = parent::reverseTransform($value);
77:
78: // application inverse du coefficient multiplicateur
79: if (($result !== null) && ($this->multiplier !== null)) $result /= $this->multiplier;
80:
81: if (\is_float($result) && ($this->precision < 1)) return (int)$result;
82: return $result;
83: }
84:
85: /**
86: * Returns a preconfigured \NumberFormatter instance.
87: *
88: * @return \NumberFormatter
89: */
90: protected function getNumberFormatter(): \NumberFormatter
91: {
92: $formatter = new \NumberFormatter($this->locale, \NumberFormatter::DECIMAL);
93:
94: if (null !== $this->precision) {
95: $formatter->setAttribute(\NumberFormatter::ROUNDING_MODE, $this->roundingMode);
96: if ($this->fixed)
97: $formatter->setAttribute(\NumberFormatter::FRACTION_DIGITS, $this->precision);
98: else
99: $formatter->setAttribute(\NumberFormatter::MAX_FRACTION_DIGITS, $this->precision);
100: }
101: $this->decimalSep = $formatter->getSymbol(\NumberFormatter::DECIMAL_SEPARATOR_SYMBOL);
102:
103: $formatter->setAttribute(\NumberFormatter::GROUPING_USED, $this->grouping);
104: if ($this->grouping) {
105: // affectation du séparateur de grouping spécifié via le constructeur
106: if ($this->groupingSep !== null) $formatter->setSymbol(\NumberFormatter::GROUPING_SEPARATOR_SYMBOL, $this->groupingSep);
107: // récupération du séparateur de grouping utilisé par défaut (selon la locale)
108: else $this->groupingSep = $formatter->getSymbol(\NumberFormatter::GROUPING_SEPARATOR_SYMBOL);
109: }
110:
111: return $formatter;
112: }
113:
114: /**
115: * Retourne le séparateur de milliers utilisé.
116: *
117: * @return string|null
118: */
119: public function getGroupingSep(): ?string
120: {
121: if (! $this->grouping) return null;
122:
123: // force l'initialisation du formatter si la valeur par défaut n'est pas encore définir
124: if ($this->groupingSep === null) $this->getNumberFormatter();
125:
126: return $this->groupingSep;
127: }
128:
129: /**
130: * Retourne le séparateur décimal utilisé.
131: *
132: * @return string|null
133: */
134: public function getDecimalSep(): ?string
135: {
136: // force l'initialisation du formatter si la valeur par défaut n'est pas encore définir
137: if ($this->decimalSep === null) $this->getNumberFormatter();
138:
139: return $this->decimalSep;
140: }
141:
142: }
143: