Skip to content

[Form] Improved ChoiceList implementation and made form naming more restrictive #3156

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 13 commits into from
Jan 28, 2012
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions CHANGELOG-2.1.md
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,45 @@ To get the diff between two versions, go to https://github.com/symfony/symfony/c
* allowed setting different options for RepeatedType fields (like the label)
* added support for empty form name at root level, this enables rendering forms
without form name prefix in field names

* [BC BREAK] made form naming more restrictive. Form and field names must
start with a letter, digit or underscore and only contain letters, digits,
underscores, hyphens and colons

* [BC BREAK] changed default name of the prototype in the "collection" type
from "$$name$$" to "__name__". No dollars are appended/prepended to custom
names anymore.

* [BC BREAK] greatly improved `ChoiceListInterface` and all of its
implementations. `EntityChoiceList` was adapted, the methods `getEntities()`,
`getEntitiesByKeys()`, `getIdentifier()` and `getIdentifierValues()` were
removed/made private. Instead of the first two you can use `getChoices()`
and `getChoicesByValues()`, for the latter two no replacement exists.
`ArrayChoiceList` was replaced by `SimpleChoiceList`.
`PaddedChoiceList`, `MonthChoiceList` and `TimezoneChoiceList` were removed.
Their functionality was merged into `DateType`, `TimeType` and `TimezoneType`.

* [BC BREAK] removed `EntitiesToArrayTransformer` and `EntityToIdTransformer`.
The former has been replaced by `CollectionToArrayTransformer` in combination
with `EntityChoiceList`, the latter is not required in the core anymore.

* [BC BREAK] renamed

* `ArrayToBooleanChoicesTransformer` to `ChoicesToBooleanArrayTransformer`
* `ScalarToBooleanChoicesTransformer` to `ChoiceToBooleanArrayTransformer`
* `ArrayToChoicesTransformer` to `ChoicesToValuesTransformer`
* `ScalarToChoiceTransformer` to `ChoiceToValueTransformer`

to be consistent with the naming in `ChoiceListInterface`.

* [BC BREAK] removed `FormUtil::toArrayKey()` and `FormUtil::toArrayKeys()`.
They were merged into `ChoiceList` and have no public equivalent anymore.

* added `ComplexChoiceList` and `ObjectChoiceList`. Both let you select amongst
objects in a choice field, but feature different constructors.
* choice fields now throw a `FormException` if neither the "choices" nor the
"choice_list" option is set
* the radio field is now a child type of the checkbox field

### HttpFoundation

Expand Down
66 changes: 65 additions & 1 deletion UPGRADE-2.1.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,4 +76,68 @@ UPGRADE FROM 2.0 to 2.1
If you don't want to set the `Valid` constraint, or if there is no reference
from the data of the parent form to the data of the child form, you can
enable BC behaviour by setting the option "cascade_validation" to `true` on
the parent form.
the parent form.

* The strategy for generating the HTML attributes "id" and "name"
of choices in a choice field has changed

Instead of appending the choice value, a generated integer is now appended
by default. Take care if your Javascript relies on that. If you can
guarantee that your choice values only contain ASCII letters, digits,
letters, colons and underscores, you can restore the old behaviour by
setting the option "index_strategy" of the choice field to
`ChoiceList::COPY_CHOICE`.

* The strategy for generating the HTML attributes "value" of choices in a
choice field has changed

Instead of using the choice value, a generated integer is now stored.
Again, take care if your Javascript reads this value. If your choice field
is a non-expanded single-choice field, or if the choices are guaranteed not
to contain the empty string '' (which is the case when you added it manually
or when the field is a single-choice field and is not required), you can
restore the old behaviour by setting the option "value_strategy" to
`ChoiceList::COPY_CHOICE`.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this a problem to have a choice-list containing an empty string ? What will happen if a choice-list contain an empty string ?

I see multiple problems with the default strategy, and I can't use the old strategy because of the empty string problem:

  1. If the order of choices change in the time between the display of a page, and the submition of a form, the wrong value will be set.
  2. It works only with browsers: if one built an API, and that API uses forms to handle data posted by clients, the clients can't know the value they have to send.
  3. How javascripts can know which generated value maps to which real value ?

Point 1 could be addressed by hashing the original value to generate the value (if values aren't objects).

Point 2 can only be addressed by not using this strategy (but what about empty strings ?)

Point 3 can be addressed by exposing the mapping to the client side, but this seems to become complicated.


* In the template of the choice type, the structure of the "choices" variable
has changed

"choices" now contains ChoiceView objects with two getters `getValue()`
and `getLabel()` to access the choice data. The indices of the array
store an index whose generation is controlled by the "index_generation"
option of the choice field.

Before:

{% for choice, label in choices %}
<option value="{{ choice }}"{% if _form_is_choice_selected(form, choice) %} selected="selected"{% endif %}>
{{ label }}
</option>
{% endfor %}

After:

{% for choice in choices %}
<option value="{{ choice.value }}"{% if _form_is_choice_selected(form, choice) %} selected="selected"{% endif %}>
{{ choice.label }}
</option>
{% endfor %}

* In the template of the collection type, the default name of the prototype
field has changed from "$$name$$" to "__name__"

For custom names, no dollars are prepended/appended anymore. You are advised
to prepend and append double underscores wherever you have configured the
prototype name manually.

Before:

$builder->add('tags', 'collection', array('prototype' => 'proto'));

// results in the name "$$proto$$" in the template

After:

$builder->add('tags', 'collection', array('prototype' => '__proto__'));

// results in the name "__proto__" in the template
Loading