Introduction
I’ve recently had to develop a form in a Symfony application that has a section of it with survey type questions. In Symfony, it’s not so obvious that the ChoiceType
field type is the most suitable type, but it definitely is the one to use. This article describes how to create the survey form.
Original Requirements
I didn’t have any written down requirements other than the existing form, and below is a screenshot of what that looks like:
As you can see, it uses checkboxes; which is not appropriate. The reason is that normally in a group of checkboxes, you can check any or all of them. In a survey, you only want to check one of them. So a radio button is the best choice.
Also look closely at the headings. The problem is the order. The order should be from one extreme to another. So instead this order:
Strongly Agree — Somewhat Agree — Somewhat Disagree — Strongly Disagree.
So my form would need to incorporate those need requirement changes.
ChoiceType in Form Class
In my form class, I initially used the following sample code for the “recommend
” set of radio button choices:
->add('sur_recommend', ChoiceType::class, array(
'mapped' => false,
'label' => 'I would recommend this program to my friends and '.
'family as a great opportunity',
'choices' => array(
'strong_A' => 'strong_A',
'some_A' => 'some_A',
'some_D' => 'some_D',
'strong_D' => 'strong_D',
),
'expanded' => true,
'multiple' => false,
))
With the two settings “expanded
” set to true
and “multiple
” set to false
, this sets the choice group to radio buttons. The “mapped
” set to false
just means that “sur_recommended
” is not a Doctrine database column, so the data is not mapped. I keep the values and the keys the same, since I don’t want to show any labels in my case.
Unfortunately with the above code, it doesn’t quite work for my case. If I render in Twig using just this code:
{{ form_widget(form.sur_recommend) }}
Then the resultant output will render like the following screenshot:
I don’t want those labels appearing at all, but instead in a column header on the top.
Fixing the Code
In order to prevent the labels from showing, you should set “choice_label
” to false
, like the following code:
->add('sur_recommend', ChoiceType::class, array(
'mapped' => false,
'label' => 'I would recommend this program to my friends and '.
'family as a great opportunity',
'choice_label' => false,
'choices' => array(
'strong_A' => 'strong_A',
'some_A' => 'some_A',
'some_D' => 'some_D',
'strong_D' => 'strong_D',
),
'expanded' => true,
'multiple' => false,
))
Then when rendered, the labels no longer appear. The other problem is, we have a label for the choice group, but we just want it to appear on the left side, and the buttons on the right. We also need the buttons to each appear under the names of each of the column headings. This needs to be done in Twig, which is described next.
Using Twig to Render Properly
Twig is a powerful rendering language, and it can greatly simply any of your programming efforts. So I highly recommend learning how to use it properly. You can always use Twigfiddle to experiment first with code that you need to write and also to help you learn.
Since I don’t render each of the choice values (which would be labels), I need to put a header column on the top, and put each of the choice group labels on the left. Then each of the radio buttons need to line up under the header columns. To do this, an HTML table is a good idea.
Here is the Twig code that I came up with:
<fieldset>
<legend>Survey:</legend>
<table>
<tr><td></td><td>Strongly Agree</td>
<td>Somewhat Agree</td>
<td>Somewhat Disagree</td><td>Strongly Disagree</td></tr>
<tr style="text-align: center;"><td>
{{ form_label(form.sur_recommend) }}</td>
{% for i in 0..3 %}
<td>{{ form_widget(form.sur_recommend[i]) }}</td>
{% endfor %}
</tr>
...
</table>
</fieldset>
Notice the heading column is in one row, and I have a td
element for each heading. I use the form_label
to show the choice group label on the left. Notice I have a for
loop, and a td
element for each radio button as part of the choice group.
The resultant code renders on a page as follows:
This is quite functional and looks good and similar to the original.