27
27
use Symfony \Component \Form \Extension \Core \DataTransformer \ChoiceToValueTransformer ;
28
28
use Symfony \Component \Form \Extension \Core \EventListener \MergeCollectionListener ;
29
29
use Symfony \Component \Form \FormBuilderInterface ;
30
+ use Symfony \Component \Form \FormError ;
30
31
use Symfony \Component \Form \FormEvent ;
31
32
use Symfony \Component \Form \FormEvents ;
32
33
use Symfony \Component \Form \FormInterface ;
33
34
use Symfony \Component \Form \FormView ;
34
35
use Symfony \Component \OptionsResolver \Options ;
35
36
use Symfony \Component \OptionsResolver \OptionsResolver ;
37
+ use Symfony \Component \Translation \TranslatorInterface as LegacyTranslatorInterface ;
38
+ use Symfony \Contracts \Translation \TranslatorInterface ;
36
39
37
40
class ChoiceType extends AbstractType
38
41
{
39
42
private $ choiceListFactory ;
43
+ private $ translator ;
40
44
41
- public function __construct (ChoiceListFactoryInterface $ choiceListFactory = null )
45
+ /**
46
+ * @param TranslatorInterface $translator
47
+ */
48
+ public function __construct (ChoiceListFactoryInterface $ choiceListFactory = null , $ translator = null )
42
49
{
43
50
$ this ->choiceListFactory = $ choiceListFactory ?: new CachingFactoryDecorator (
44
51
new PropertyAccessDecorator (
45
52
new DefaultChoiceListFactory ()
46
53
)
47
54
);
55
+
56
+ if (null !== $ translator && !$ translator instanceof LegacyTranslatorInterface && !$ translator instanceof TranslatorInterface) {
57
+ throw new \TypeError (sprintf ('Argument 2 passed to "%s()" must be an instance of "%s", "%s" given. ' , __METHOD__ , TranslatorInterface::class, \is_object ($ translator ) ? \get_class ($ translator ) : \gettype ($ translator )));
58
+ }
59
+ $ this ->translator = $ translator ;
48
60
}
49
61
50
62
/**
51
63
* {@inheritdoc}
52
64
*/
53
65
public function buildForm (FormBuilderInterface $ builder , array $ options )
54
66
{
67
+ $ unknownValues = [];
55
68
$ choiceList = $ this ->createChoiceList ($ options );
56
69
$ builder ->setAttribute ('choice_list ' , $ choiceList );
57
70
@@ -79,10 +92,12 @@ public function buildForm(FormBuilderInterface $builder, array $options)
79
92
80
93
$ this ->addSubForms ($ builder , $ choiceListView ->preferredChoices , $ options );
81
94
$ this ->addSubForms ($ builder , $ choiceListView ->choices , $ options );
95
+ }
82
96
97
+ if ($ options ['expanded ' ] || $ options ['multiple ' ]) {
83
98
// Make sure that scalar, submitted values are converted to arrays
84
99
// which can be submitted to the checkboxes/radio buttons
85
- $ builder ->addEventListener (FormEvents::PRE_SUBMIT , function (FormEvent $ event ) {
100
+ $ builder ->addEventListener (FormEvents::PRE_SUBMIT , function (FormEvent $ event ) use ( $ choiceList , $ options , & $ unknownValues ) {
86
101
$ form = $ event ->getForm ();
87
102
$ data = $ event ->getData ();
88
103
@@ -97,6 +112,10 @@ public function buildForm(FormBuilderInterface $builder, array $options)
97
112
// Convert the submitted data to a string, if scalar, before
98
113
// casting it to an array
99
114
if (!\is_array ($ data )) {
115
+ if ($ options ['multiple ' ]) {
116
+ throw new TransformationFailedException ('Expected an array. ' );
117
+ }
118
+
100
119
$ data = (array ) (string ) $ data ;
101
120
}
102
121
@@ -108,34 +127,61 @@ public function buildForm(FormBuilderInterface $builder, array $options)
108
127
$ unknownValues = $ valueMap ;
109
128
110
129
// Reconstruct the data as mapping from child names to values
111
- $ data = [];
112
-
113
- /** @var FormInterface $child */
114
- foreach ($ form as $ child ) {
115
- $ value = $ child ->getConfig ()->getOption ('value ' );
116
-
117
- // Add the value to $data with the child's name as key
118
- if (isset ($ valueMap [$ value ])) {
119
- $ data [$ child ->getName ()] = $ value ;
120
- unset($ unknownValues [$ value ]);
121
- continue ;
130
+ $ knownValues = [];
131
+
132
+ if ($ options ['expanded ' ]) {
133
+ /** @var FormInterface $child */
134
+ foreach ($ form as $ child ) {
135
+ $ value = $ child ->getConfig ()->getOption ('value ' );
136
+
137
+ // Add the value to $data with the child's name as key
138
+ if (isset ($ valueMap [$ value ])) {
139
+ $ knownValues [$ child ->getName ()] = $ value ;
140
+ unset($ unknownValues [$ value ]);
141
+ continue ;
142
+ }
143
+ }
144
+ } else {
145
+ foreach ($ data as $ value ) {
146
+ if ($ choiceList ->getChoicesForValues ([$ value ])) {
147
+ $ knownValues [] = $ value ;
148
+ unset($ unknownValues [$ value ]);
149
+ }
122
150
}
123
151
}
124
152
125
153
// The empty value is always known, independent of whether a
126
154
// field exists for it or not
127
155
unset($ unknownValues ['' ]);
128
156
129
- // Throw exception if unknown values were submitted
130
- if (\count ($ unknownValues ) > 0 ) {
157
+ // Throw exception if unknown values were submitted (multiple choices will be handled in a different event listener below)
158
+ if (\count ($ unknownValues ) > 0 && ! $ options [ ' multiple ' ] ) {
131
159
throw new TransformationFailedException (sprintf ('The choices "%s" do not exist in the choice list. ' , implode ('", " ' , array_keys ($ unknownValues ))));
132
160
}
133
161
134
- $ event ->setData ($ data );
162
+ $ event ->setData ($ knownValues );
135
163
});
136
164
}
137
165
138
166
if ($ options ['multiple ' ]) {
167
+ $ builder ->addEventListener (FormEvents::POST_SUBMIT , function (FormEvent $ event ) use (&$ unknownValues ) {
168
+ // Throw exception if unknown values were submitted
169
+ if (\count ($ unknownValues ) > 0 ) {
170
+ $ form = $ event ->getForm ();
171
+
172
+ $ clientDataAsString = is_scalar ($ form ->getViewData ()) ? (string ) $ form ->getViewData () : \gettype ($ form ->getViewData ());
173
+ $ messageTemplate = 'The value {{ value }} is not valid. ' ;
174
+
175
+ if (null !== $ this ->translator ) {
176
+ $ message = $ this ->translator ->trans ($ messageTemplate , ['{{ value }} ' => $ clientDataAsString ], 'validators ' );
177
+ } else {
178
+ $ message = strtr ($ messageTemplate , ['{{ value }} ' => $ clientDataAsString ]);
179
+ }
180
+
181
+ $ form ->addError (new FormError ($ message , $ messageTemplate , ['{{ value }} ' => $ clientDataAsString ], null , new TransformationFailedException (sprintf ('The choices "%s" do not exist in the choice list. ' , implode ('", " ' , array_keys ($ unknownValues ))))));
182
+ }
183
+ });
184
+
139
185
// <select> tag with "multiple" option or list of checkbox inputs
140
186
$ builder ->addViewTransformer (new ChoicesToValuesTransformer ($ choiceList ));
141
187
} else {
0 commit comments