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