Skip to content

Commit 6da211f

Browse files
committed
[Form] Fix ChoiceType options
1 parent e7246b6 commit 6da211f

File tree

7 files changed

+191
-78
lines changed

7 files changed

+191
-78
lines changed

reference/forms/types/choice.rst

Lines changed: 81 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -70,10 +70,21 @@ This will create a ``select`` drop-down like this:
7070
.. image:: /images/reference/form/choice-example1.png
7171
:align: center
7272

73-
If the user selects ``No``, the form will return ``false`` for this field. Similarly,
74-
if the starting data for this field is ``true``, then ``Yes`` will be auto-selected.
75-
In other words, the **value** of each item is the value you want to get/set in PHP
76-
code, while the **key** is what will be shown to the user.
73+
The model data of this field, the **choice** may be any of the ``choices`` option
74+
values, while **keys** are used as default label that the user will see and select.
75+
If the user selects ``No``, the form will return a string representation of that
76+
choice, a string **value** (html attribute) which is by default the **choice** data
77+
casted to string.
78+
Hence, when choices are objects, either they should implement ``__toString()``
79+
method or the option ``choice_value`` should be defined.
80+
When the **choices** cannot be casted to string or when ``choice_value`` returns
81+
either ``null`` or non unique values an incrementing integer is used as **value**.
82+
83+
If the starting data for this field is ``true``, then ``Yes`` will be auto-selected.
84+
In other words, each value of the ``choices`` option is the **choice** data you
85+
want to deal with in PHP code, while the **key** is the default label that will be
86+
shown to the user and the **value** is the string that will be submitted to the
87+
form and used in the template for the corresponding html attribute.
7788

7889
.. caution::
7990

@@ -83,40 +94,68 @@ code, while the **key** is what will be shown to the user.
8394
and will be removed in 3.0. To read about the old API, read an older version of
8495
the docs.
8596

97+
.. note::
98+
99+
Pre selected choices will depend on the **data** passed to field and the values of
100+
the ``choices`` option. However submitted choices will depend on the **string**
101+
matching the **choice**. In the example above, the default values falls back on an
102+
incrementing integer because ``null`` cannot be casted to string.
103+
You should consider it as well when dealing with ``emtpy_data`` option::
104+
105+
$builder->add('isAttending', 'choice', array(
106+
'choices' => array(
107+
'Maybe' => null,
108+
'Yes' => true,
109+
'No' => false,
110+
),
111+
'choices_as_values' => true,
112+
'data' => true, // pre selected choice
113+
'empty_data' => '1', // default submitted value
114+
));
115+
116+
Also note that as a ``scalar``, ``false`` string **value** is by default ``"0"``
117+
to avoid conflict with placeholder value which is always an empty string.
118+
86119
Advanced Example (with Objects!)
87120
--------------------------------
88121

89122
This field has a *lot* of options and most control how the field is displayed. In
90123
this example, the underlying data is some ``Category`` object that has a ``getName()``
91124
method::
92125

93-
$builder->add('category', 'choice', [
94-
'choices' => [
126+
$builder->add('category', 'choice', array(
127+
'choices' => array(
95128
new Category('Cat1'),
96129
new Category('Cat2'),
97130
new Category('Cat3'),
98131
new Category('Cat4'),
99-
],
132+
),
100133
'choices_as_values' => true,
101-
'choice_label' => function($category, $key, $index) {
134+
'choice_label' => function($category, $key, $value) {
102135
/** @var Category $category */
103136
return strtoupper($category->getName());
104137
},
105-
'choice_attr' => function($category, $key, $index) {
106-
return ['class' => 'category_'.strtolower($category->getName())];
138+
'choice_attr' => function($category, $key, $value) {
139+
return array('class' => 'category_'.strtolower($category->getName()));
107140
},
108-
'group_by' => function($category, $key, $index) {
141+
'group_by' => function($category, $key, $value) {
109142
// randomly assign things into 2 groups
110143
return rand(0, 1) == 1 ? 'Group A' : 'Group B'
111144
},
112-
'preferred_choices' => function($category, $key, $index) {
145+
'preferred_choices' => function($category, $key, $value) {
113146
return $category->getName() == 'Cat2' || $category->getName() == 'Cat3';
114147
},
115-
]);
148+
));
116149

117150
You can also customize the `choice_name`_ and `choice_value`_ of each choice if
118151
you need further HTML customization.
119152

153+
.. caution::
154+
155+
We avoid type hinting ``Category`` in this example as it may conflict
156+
if the placeholder is passed, also you should test whether ``$choice``
157+
is ``null`` before calling any method or object property.
158+
120159
.. _forms-reference-choice-tags:
121160

122161
.. include:: /reference/forms/types/options/select_how_rendered.rst.inc
@@ -135,19 +174,19 @@ Grouping Options
135174

136175
You can easily "group" options in a select by passing a multi-dimensional choices array::
137176

138-
$builder->add('stockStatus', 'choice', [
139-
'choices' => [
140-
'Main Statuses' => [
177+
$builder->add('stockStatus', 'choice', array(
178+
'choices' => array(
179+
'Main Statuses' => array(
141180
'Yes' => 'stock_yes',
142181
'No' => 'stock_no',
143-
],
144-
'Out of Stock Statuses' => [
182+
),
183+
'Out of Stock Statuses' => array(
145184
'Backordered' => 'stock_backordered',
146185
'Discontinued' => 'stock_discontinued',
147-
]
148-
],
186+
),
187+
),
149188
'choices_as_values' => true,
150-
);
189+
));
151190

152191
.. image:: /images/reference/form/choice-example4.png
153192
:align: center
@@ -160,18 +199,24 @@ Field Options
160199
choices
161200
~~~~~~~
162201

163-
**type**: ``array`` **default**: ``array()``
202+
**type**: ``array`` or ``\Traversable`` **default**: ``array()``
164203

165204
This is the most basic way to specify the choices that should be used
166205
by this field. The ``choices`` option is an array, where the array key
167-
is the item's label and the array value is the item's value::
206+
is the choice's label and the array value is the choice's data::
168207

169208
$builder->add('inStock', 'choice', array(
170-
'choices' => array('In Stock' => true, 'Out of Stock' => false),
209+
'choices' => array(
210+
'In Stock' => true,
211+
'Out of Stock' => false,
212+
),
171213
// always include this
172214
'choices_as_values' => true,
173215
));
174216

217+
The component will try to cast the choices data to string to use it in view
218+
format, but you can customize it using the `choice_value`_ option.
219+
175220
.. include:: /reference/forms/types/options/choice_attr.rst.inc
176221

177222
.. _reference-form-choice-label:
@@ -229,9 +274,14 @@ choice_loader
229274

230275
**type**: :class:`Symfony\\Component\\Form\\ChoiceList\\Loader\\ChoiceLoaderInterface`
231276

232-
The ``choice_loader`` can be used to only partially load the choices in cases where
233-
a fully-loaded list is not necessary. This is only needed in advanced cases and
234-
would replace the ``choices`` option.
277+
The ``choice_loader`` can be used to load the choices form a data source with a
278+
custom logic (e.g query language) such as database or search engine.
279+
The list will be fully loaded to display the form, but while submission only the
280+
submitted choices will be loaded.
281+
282+
Also, the :class:``Symfony\\Component\\Form\\ChoiceList\\Factory\\ChoiceListFactoryInterface`` will cache the choice list
283+
so the same :class:``Symfony\\Component\\Form\\ChoiceList\\Loader\\ChoiceLoaderInterface`` can be used in different fields with more performance
284+
(reducing N queries to 1).
235285

236286
.. include:: /reference/forms/types/options/choice_name.rst.inc
237287

@@ -250,8 +300,8 @@ choices_as_values
250300

251301
The ``choices_as_values`` option was added to keep backward compatibility with the
252302
*old* way of handling the ``choices`` option. When set to ``false`` (or omitted),
253-
the choice keys are used as the underlying value and the choice values are shown
254-
to the user.
303+
the choice keys are used as the view value and the choice values are shown
304+
to the user as label.
255305

256306
* Before 2.7 (and deprecated now)::
257307

@@ -271,12 +321,11 @@ to the user.
271321
'choices_as_values' => true,
272322
));
273323

274-
In Symfony 3.0, the ``choices_as_values`` option doesn't exist, but the ``choice``
275-
type behaves as if it were set to true:
324+
As of Symfony 3.0, the ``choices_as_values`` option is ``true`` by default:
276325

277326
* Default for 3.0::
278327

279-
$builder->add('gender', 'choice', array(
328+
$builder->add('gender', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', array(
280329
'choices' => array('Male' => 'm', 'Female' => 'f'),
281330
));
282331

reference/forms/types/options/choice_attr.rst.inc

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,12 @@ choice_attr
44
.. versionadded:: 2.7
55
The ``choice_attr`` option was introduced in Symfony 2.7.
66

7-
**type**: ``array``, ``callable`` or ``string`` **default**: ``array()``
7+
**type**: ``array``, ``callable``, ``string`` or :class:``Symfony\\Component\\PropertyAccess\\PropertyPath`` **default**: ``array()``
88

9-
Use this to add additional HTML attributes to each choice. This can be an array
10-
of attributes (if they are the same for each choice), a callable or a property path
11-
(just like `choice_label`_).
9+
Use this to add additional HTML attributes to each choice. This can be used as
10+
a callable or a property path (just like `choice_label`_).
1211

13-
If an array, the keys of the ``choices`` array must be used as keys::
12+
Also, if used as an array, the keys of the ``choices`` array must be used as keys::
1413

1514
$builder->add('attending', 'choice', array(
1615
'choices' => array(
@@ -19,8 +18,21 @@ If an array, the keys of the ``choices`` array must be used as keys::
1918
'Maybe' => null,
2019
),
2120
'choices_as_values' => true,
22-
'choice_attr' => function($val, $key, $index) {
23-
// adds a class like attending_yes, attending_no, etc
24-
return ['class' => 'attending_'.strtolower($key)];
21+
'choice_attr' => array(
22+
// will be used for the second choice
23+
'No' => array('class' => 'singular_choice_option');
24+
),
25+
));
26+
27+
$builder->add('attending', 'choice', array(
28+
'choices' => array(
29+
'Yes' => true,
30+
'No' => false,
31+
'Maybe' => null,
32+
),
33+
'choices_as_values' => true,
34+
'choice_attr' => function() {
35+
// will be used for all choices
36+
return array('class' => 'choice_option');
2537
},
2638
));

reference/forms/types/options/choice_label.rst.inc

Lines changed: 33 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,29 @@ choice_label
44
.. versionadded:: 2.7
55
The ``choice_label`` option was introduced in Symfony 2.7.
66

7-
**type**: ``string``, ``callable`` or ``false`` **default**: ``null``
7+
**type**: ``string``, ``callable``, :class:``Symfony\\Component\\PropertyAccess\\PropertyPath`` or ``false`` **default**: ``null``
88

99
Normally, the array key of each item in the ``choices`` option is used as the
1010
text that's shown to the user. The ``choice_label`` option allows you to take
11-
more control::
11+
more control.
12+
13+
If your choice values are objects (default in ``EntityType``), then ``choice_label``
14+
can be a string :ref:`property path <reference-form-option-property-path>`. Imagine you have some
15+
``Status`` class with a ``getDisplayName()`` method::
16+
17+
$builder->add('attending', 'choice', array(
18+
'choices' => array(
19+
new Status(Status::YES),
20+
new Status(Status::NO),
21+
new Status(Status::MAYBE),
22+
),
23+
'choices_as_values' => true,
24+
'choice_label' => 'displayName',
25+
));
26+
27+
If set as a callable is called for each choice, passing you its model data ``$choice``
28+
and the ``$key`` from the choices array (default label). ``$value`` is either
29+
`choice_value`_ option value when defined or the default string value of the choice::
1230

1331
$builder->add('attending', 'choice', array(
1432
'choices' => array(
@@ -17,8 +35,8 @@ more control::
1735
'maybe' => null,
1836
),
1937
'choices_as_values' => true,
20-
'choice_label' => function ($value, $key, $index) {
21-
if ($value == true) {
38+
'choice_label' => function ($choice, $key, $value) {
39+
if (true === $choice) {
2240
return 'Definitely!';
2341
}
2442
return strtoupper($key);
@@ -28,26 +46,20 @@ more control::
2846
},
2947
));
3048

31-
This method is called for *each* choice, passing you the choice ``$value`` and the
32-
``$key`` from the choices array (``$index`` is related to `choice_value`_). This
33-
will give you:
49+
The example above would output:
3450

3551
.. image:: /images/reference/form/choice-example2.png
3652
:align: center
3753

38-
If your choice values are objects, then ``choice_label`` can also be a
39-
:ref:`property path <reference-form-option-property-path>`. Imagine you have some
40-
``Status`` class with a ``getDisplayName()`` method::
41-
42-
$builder->add('attending', 'choice', array(
43-
'choices' => array(
44-
new Status(Status::YES),
45-
new Status(Status::NO),
46-
new Status(Status::MAYBE),
47-
),
48-
'choices_as_values' => true,
49-
'choice_label' => 'displayName',
50-
));
51-
5254
If set to ``false``, all the tag labels will be discarded for radio or checkbox
5355
inputs. You can also return ``false`` from the callable to discard certain labels.
56+
57+
.. caution::
58+
59+
If you want to pass a string property path wich is also a callable (e.g 'range'),
60+
the component will treat it as a callable. You should pass a :class:``Symfony\\Component\\PropertyAccess\\PropertyPath``
61+
object to ensure the expected behavior::
62+
63+
use Symfony\Component\PropertyAccess\PropertyPath;
64+
65+
'choice_label' => new PropertyPath('range'),

reference/forms/types/options/choice_name.rst.inc

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,13 @@ choice_name
44
.. versionadded:: 2.7
55
The ``choice_name`` option was introduced in Symfony 2.7.
66

7-
**type**: ``callable`` or ``string`` **default**: ``null``
7+
**type**: ``callable``, ``string`` or :class:``Symfony\\Component\\PropertyAccess\\PropertyPath`` **default**: ``null``
88

9-
Controls the internal field name of the choice. You normally don't care about this,
10-
but in some advanced cases, you might. For example, this "name" becomes the index
11-
of the choice views in the template.
9+
Controls the internal field name of the choice. You normally don't care about
10+
this, but in some advanced cases, you might. For example, this "name" becomes
11+
the index of the choice views in the template.
1212
13-
This can be a callable or a property path. See `choice_label`_ for similar usage.
14-
If ``null`` is used, an incrementing integer is used as the name.
13+
This can be a callable or a property path. Both needs to return a non empty
14+
string, if ``null`` is used, an incrementing integer is used as the name.
15+
16+
See `choice_label`_ for similar usage.

0 commit comments

Comments
 (0)