1: <?php
2: namespace TIC\FormBundle\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: * Constructor.
21: */
22: public function __construct(int $scale = null, ?bool $grouping = false, ?int $roundingMode = \NumberFormatter::ROUND_HALFUP, $multiplier = null, mixed $locale = null, $fixed = false)
23: {
24: // détermination de la locale à utiliser (null|true automatique ; false 'en')
25: if ($locale === null || $locale === true) $this->locale = \Locale::getDefault();
26: elseif ($locale === false) $this->locale = 'en';
27: else $this->locale = $locale;
28:
29: parent::__construct($scale, $grouping, $roundingMode, $locale);
30: $this->precision = $scale;
31:
32: // le paramètre $grouping peut aussi servir à spécifier le séparateur à utiliser
33: if (! is_bool($this->grouping)) {
34: $this->groupingSep = $this->grouping;
35: $this->grouping = true;
36: }
37:
38: // coefficient multiplicateur à appliquer sur la valeur à afficher
39: if (! empty($multiplier) && is_numeric($multiplier)) $this->multiplier = $multiplier;
40:
41: $this->fixed = $fixed;
42: }
43:
44: /**
45: * Transforms a number type into localized number.
46: * @param int|float $value Number value
47: * @return string Localized value
48: * @throws TransformationFailedException If the given value is not numeric
49: * or if the value can not be transformed.
50: */
51: public function transform(mixed $value): string
52: {
53:
54: // application du coefficient multiplicateur
55: if (($value !== null) && is_numeric($value) && ($this->multiplier !== null)) $value *= $this->multiplier;
56:
57: return parent::transform($value);
58: }
59:
60: /**
61: * Transforms a localized number into an integer or float.
62: * @param string $value The localized value
63: * @return int|float The numeric value
64: * @throws TransformationFailedException If the given value is not a string
65: * or if the value can not be transformed.
66: */
67: public function reverseTransform(mixed $value): int|float|null
68: {
69: $result = parent::reverseTransform($value);
70:
71: // application inverse du coefficient multiplicateur
72: if (($result !== null) && ($this->multiplier !== null)) $result /= $this->multiplier;
73:
74: if (is_float($result) && ($this->precision < 1)) return (int)$result;
75: return $result;
76: }
77:
78: /**
79: * Returns a preconfigured \NumberFormatter instance.
80: */
81: protected function getNumberFormatter(): \NumberFormatter
82: {
83: $formatter = new \NumberFormatter($this->locale ?? \Locale::getDefault(), \NumberFormatter::DECIMAL);
84:
85: if (null !== $this->precision) {
86: $formatter->setAttribute(\NumberFormatter::ROUNDING_MODE, $this->roundingMode);
87: if ($this->fixed)
88: $formatter->setAttribute(\NumberFormatter::FRACTION_DIGITS, $this->precision);
89: else
90: $formatter->setAttribute(\NumberFormatter::MAX_FRACTION_DIGITS, $this->precision);
91: }
92:
93: $formatter->setAttribute(\NumberFormatter::GROUPING_USED, $this->grouping);
94:
95: if ($this->grouping) {
96: // affectation du séparateur de grouping spécifié via le constructeur
97: if ($this->groupingSep !== null) $formatter->setSymbol(\NumberFormatter::GROUPING_SEPARATOR_SYMBOL, $this->groupingSep);
98: // récupération du séparateur de grouping utilisé par défaut (selon la locale)
99: else $this->groupingSep = $formatter->getSymbol(\NumberFormatter::GROUPING_SEPARATOR_SYMBOL);
100: }
101:
102: $this->decimalSep = $formatter->getSymbol(\NumberFormatter::DECIMAL_SEPARATOR_SYMBOL);
103:
104: return $formatter;
105: }
106:
107: /**
108: * Retourne le séparateur de milliers utilisé.
109: * @return string|null
110: */
111: public function getGroupingSep()
112: {
113: if (! $this->grouping) return;
114:
115: // force l'initialisation du formatter si la valeur par défaut n'est pas encore définir
116: if ($this->groupingSep === null) $this->getNumberFormatter();
117:
118: return $this->groupingSep;
119: }
120:
121: /**
122: * Retourne le séparateur décimal utilisé.
123: * @return string|null
124: */
125: public function getDecimalSep()
126: {
127: // force l'initialisation du formatter si la valeur par défaut n'est pas encore définir
128: if ($this->decimalSep === null) $this->getNumberFormatter();
129:
130: return $this->decimalSep;
131: }
132:
133: }
134: