10
10
namespace Infinite \FormBundle \Form \Type ;
11
11
12
12
use Infinite \FormBundle \Form \DataTransformer \CheckboxGridTransformer ;
13
+ use Infinite \FormBundle \Form \Util \ChoiceListViewAdapter ;
13
14
use Infinite \FormBundle \Form \Util \LegacyFormUtil ;
14
15
use Symfony \Component \Form \AbstractType ;
16
+ use Symfony \Component \Form \ChoiceList \ArrayChoiceList ;
17
+ use Symfony \Component \Form \ChoiceList \ChoiceListInterface ;
18
+ use Symfony \Component \Form \ChoiceList \Factory \DefaultChoiceListFactory ;
19
+ use Symfony \Component \Form \ChoiceList \View \ChoiceListView ;
20
+ use Symfony \Component \Form \Extension \Core \ChoiceList \ChoiceListInterface as LegacyChoiceListInterface ;
21
+ use Symfony \Component \Form \Extension \Core \ChoiceList \SimpleChoiceList ;
15
22
use Symfony \Component \Form \FormBuilderInterface ;
16
23
use Symfony \Component \Form \FormInterface ;
17
24
use Symfony \Component \Form \FormView ;
25
+ use Symfony \Component \OptionsResolver \Exception \InvalidOptionsException ;
26
+ use Symfony \Component \OptionsResolver \Options ;
18
27
use Symfony \Component \OptionsResolver \OptionsResolver ;
19
28
use Symfony \Component \OptionsResolver \OptionsResolverInterface ;
29
+ use Symfony \Component \PropertyAccess \PropertyAccess ;
30
+ use Symfony \Component \PropertyAccess \PropertyAccessor ;
31
+ use Symfony \Component \PropertyAccess \PropertyPath ;
20
32
21
33
/**
22
- * Provides a checkbox grid for non-Doctrine objects (rows and cols set by x/y_choice_list )
34
+ * Provides a checkbox grid for non-Doctrine objects (rows and cols set by x/y_choices )
23
35
*/
24
36
class CheckboxGridType extends AbstractType
25
37
{
26
38
public function buildForm (FormBuilderInterface $ builder , array $ options )
27
39
{
28
- if ($ options ['x_choice_list ' ]->getPreferredViews ()) {
29
- throw new \Exception ('Checkbox grid: x choice list cannot have preferred views ' );
30
- }
40
+ $ accessor = PropertyAccess::createPropertyAccessor ();
31
41
32
- if ($ options ['y_choice_list ' ]->getPreferredViews ()) {
33
- throw new \Exception ('Checkbox grid: y choice list cannot have preferred views ' );
34
- }
42
+ /** @var ChoiceListInterface|LegacyChoiceListInterface $yChoiceList */
43
+ $ yChoiceList = $ options ['y_choice_list ' ];
44
+
45
+ if ($ yChoiceList instanceof LegacyChoiceListInterface) {
46
+ // LegacyChoiceList is repsonsible for providing labels.
47
+ foreach ($ yChoiceList ->getRemainingViews () as $ view ) {
48
+ $ labelBase = $ view ->label ;
49
+ $ value = $ view ->value ;
50
+ $ choice = $ view ->data ;
51
+
52
+ $ this ->buildRow ($ builder , $ options , $ choice , $ labelBase , $ accessor , $ value );
53
+ }
54
+ } else {
55
+ foreach ($ yChoiceList ->getChoices () as $ value => $ choice ) {
56
+ // The $choice object itself can be used as a label if it has __toString or a label path
57
+ $ labelBase = $ choice ;
35
58
36
- foreach ($ options ['y_choice_list ' ]->getRemainingViews () as $ choice ) {
37
- $ rowOptions = array (
38
- 'cell_filter ' => $ options ['cell_filter ' ],
39
- 'choice_list ' => $ options ['x_choice_list ' ],
40
- 'row ' => $ choice ,
41
- );
59
+ // Although if we're using y_choices then look up the label there.
60
+ if ($ options ['y_choices ' ] !== null && array_key_exists ($ choice , $ options ['y_choices ' ])) {
61
+ $ labelBase = $ options ['y_choices ' ][$ choice ];
62
+ }
42
63
43
- $ builder ->add ($ choice ->value , LegacyFormUtil::getType ('Infinite\FormBundle\Form\Type\CheckboxRowType ' ), $ rowOptions );
64
+ $ this ->buildRow ($ builder , $ options , $ choice , $ labelBase , $ accessor , $ value );
65
+ }
44
66
}
45
67
46
68
$ builder ->addViewTransformer (new CheckboxGridTransformer ($ options ));
47
69
}
48
70
71
+ /**
72
+ * @param FormBuilderInterface $builder
73
+ * @param array $options
74
+ * @param $choice
75
+ * @param $labelBase
76
+ * @param $accessor
77
+ * @param $value
78
+ */
79
+ protected function buildRow (FormBuilderInterface $ builder , array $ options , $ choice , $ labelBase , PropertyAccessor $ accessor , $ value ) {
80
+ $ rowOptions = array (
81
+ 'cell_filter ' => $ options ['cell_filter ' ],
82
+ 'choice_list ' => $ options ['x_choice_list ' ],
83
+ 'label_path ' => $ options ['x_label_path ' ],
84
+ 'row ' => $ choice ,
85
+ 'row_label ' => $ options ['y_label_path ' ] === null ? $ labelBase : $ accessor ->getValue ($ choice , $ options ['y_label_path ' ]),
86
+ );
87
+
88
+ $ builder ->add ($ value , LegacyFormUtil::getType ( 'Infinite\FormBundle\Form\Type\CheckboxRowType ' ), $ rowOptions );
89
+ }
90
+
91
+
49
92
public function buildView (FormView $ view , FormInterface $ form , array $ options )
50
93
{
51
- $ view ->vars ['headers ' ] = $ options ['x_choice_list ' ];
94
+ $ view ->vars ['headers ' ] = $ this -> buildChoiceListView ( $ options ['x_choice_list ' ], $ options [ ' x_choices ' ], $ options [ ' x_label_path ' ]) ;
52
95
}
53
96
54
97
public function getBlockPrefix ()
@@ -58,19 +101,55 @@ public function getBlockPrefix()
58
101
59
102
public function configureOptions (OptionsResolver $ resolver )
60
103
{
104
+ $ defaultXChoiceList = function (Options $ options ) {
105
+ if (!isset ($ options ['x_choices ' ])) {
106
+ throw new InvalidOptionsException ('You must provide the x_choices option. ' );
107
+ }
108
+
109
+ // SF 2.7+: choice lists are not responsible for labels.
110
+ // Strip the labels until we build the choice view later.
111
+ if (class_exists ('Symfony\Component\Form\ChoiceList\ArrayChoiceList ' )) {
112
+ return new ArrayChoiceList (array_keys ($ options ['x_choices ' ]), function ($ choice ) { return $ choice ; });
113
+ }
114
+
115
+ // BC < SF 2.7
116
+ return new SimpleChoiceList ($ options ['x_choices ' ]);
117
+ };
118
+
119
+ $ defaultYChoiceList = function (Options $ options ) {
120
+ if (!isset ($ options ['y_choices ' ])) {
121
+ throw new InvalidOptionsException ('You must provide the y_choices option. ' );
122
+ }
123
+
124
+ // SF 2.7+: choice lists are not responsible for labels.
125
+ // Strip the labels for now and look them up later.
126
+ if (class_exists ('Symfony\Component\Form\ChoiceList\ArrayChoiceList ' )) {
127
+ return new ArrayChoiceList (array_keys ($ options ['y_choices ' ]), function ($ choice ) { return $ choice ; });
128
+ }
129
+
130
+ // BC < SF 2.7
131
+ return new SimpleChoiceList ($ options ['y_choices ' ]);
132
+ };
133
+
61
134
$ resolver ->setDefaults (array (
62
135
'class ' => null ,
63
136
'cell_filter ' => null ,
137
+
138
+ 'x_choices ' => null ,
139
+ 'x_choice_list ' => $ defaultXChoiceList ,
140
+ 'x_label_path ' => null ,
141
+
142
+ 'y_label_path ' => null ,
143
+ 'y_choices ' => null ,
144
+ 'y_choice_list ' => $ defaultYChoiceList ,
64
145
));
65
146
66
147
$ resolver ->setRequired (array (
67
- 'x_choice_list ' ,
68
148
'x_path ' ,
69
- 'y_choice_list ' ,
70
149
'y_path ' ,
71
150
));
72
151
}
73
-
152
+
74
153
// BC for SF < 2.7
75
154
public function setDefaultOptions (OptionsResolverInterface $ resolver )
76
155
{
@@ -82,4 +161,40 @@ public function getName()
82
161
{
83
162
return $ this ->getBlockPrefix ();
84
163
}
164
+
165
+ /**
166
+ * @param ChoiceListInterface|LegacyChoiceListInterface $choiceList
167
+ * @param array|null $originalChoices
168
+ * @param string|PropertyPath|null $labelPath
169
+ * @return ChoiceListView|LegacyChoiceListInterface
170
+ */
171
+ protected function buildChoiceListView ($ choiceList , $ originalChoices , $ labelPath )
172
+ {
173
+ // BC for SF < 2.7
174
+ if ($ choiceList instanceof LegacyChoiceListInterface) {
175
+ return $ choiceList ;
176
+ }
177
+
178
+ // Build the choice list view the usual way.
179
+ $ accessor = PropertyAccess::createPropertyAccessor ();
180
+
181
+ $ choiceListFactory = new DefaultChoiceListFactory ();
182
+ $ labelCallback = function ($ choice ) use ($ accessor , $ originalChoices , $ labelPath ) {
183
+ // If we stripped the choice labels back in configureOptions then look it up now.
184
+ if ($ originalChoices !== null && array_key_exists ($ choice , $ originalChoices )) {
185
+ $ choice = $ originalChoices [$ choice ];
186
+ }
187
+
188
+ if ($ labelPath === null ) {
189
+ return (string )$ choice ;
190
+ } else {
191
+ return $ accessor ->getValue ($ choice , $ labelPath );
192
+ }
193
+ };
194
+
195
+ $ choiceListView = $ choiceListFactory ->createView ($ choiceList , null , $ labelCallback );
196
+
197
+ // Wrap it in a custom class so that old form_themes can still call getRemainingViews.
198
+ return new ChoiceListViewAdapter ($ choiceListView );
199
+ }
85
200
}
0 commit comments