-
-
Notifications
You must be signed in to change notification settings - Fork 9.6k
[Form] Add "choice_labels", "choice_values", "choice_attr" and "group_by" options to ChoiceType #4067
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
Comments
I think A is more readable. |
Option A because the other doesn't seem intuitive... |
Option A without a doubt, It may be more verbose but a lot more readable as well. |
We need to be explicit. No magic or whatsoever. So A. |
Obviously A |
A 👍 |
Just for the record, you can also explicitely put the indices, so expliciteness is no argument here. I updated the snippets above. |
I think A is easier to read and also easier to build dynamically. Because in B you need to keep track of the indicies across groups. So simply |
It's A for me :) |
A is so intuitive! |
As stated @datiecher, A is more readable. So A for me. |
A looking good for me. |
Poll closed. |
I guess I'm too late to the party, but what about translations? |
Translations are done in the template. |
Any news on this? |
I was working on this again, but then stopped at the feature freeze of 2.2. Hopefully 2.3 then. |
2.5 perhaps? |
It seems not. As 2.5 is feature freeze. It's a shame as there is a lot of long standing issues. |
Isn't it possible to split this one into multiple smaller tasks? I would love to see a more flexible way to format choices labels, e.g. with a closure in the property field. There seem to be closed a couple of working PR's in favor of this ticket. As i understand that this tickets aims for a more generic solution it seems that this cannot be done in a timely manner. |
@krizon the issue if we introduce specific configuration ways, we cannot remove them in favor of the generic one later, because it would be a BC break. |
@webmozart: Any news on this? |
I would like to see this feature in Symfony 2.6. We are a bit limited for the moment. |
I could use this very much right now. Great idea! |
@webmozart when do you plan to work on this again ? |
@webmozart What would be the simplest way to implement this sort of feature using standard form theming? Perhaps we can point people to an example of how it might be done. It seems that part of the issue here is that you end up having to off-load very view specific data onto the entity, particularly for something like |
@ryancastle I'm currently working on an implementation. I'll hopefully upload a PR today. |
See the PR here: #12148 |
So from what I see, the poll about passing labels is not relevant anymore because they are passed as generic callable or propertypath. |
@Tobion Yes. Passing labels as separate array becomes superfluous if we flip the "choices" array. |
@webmozart: Any chances of getting this PR and the ones depending on it through so they can be in 2.7? |
Working on it. |
…l, value, index and attribute generation (webmozart) This PR was merged into the 2.7 branch. Discussion ---------- [Form] Refactored choice lists to support dynamic label, value, index and attribute generation This is a rebase of #12148 on the 2.7 branch. | Q | A | ------------- | --- | Bug fix? | no | New feature? | yes | BC breaks? | yes | Deprecations? | yes | Tests pass? | yes | Fixed tickets | #4067, #5494, #3836, #8658, #12148 | License | MIT | Doc PR | TODO I implemented the additional options "choice_label", "choice_name", "choice_value", "choice_attr", "group_by" and "choices_as_values" for ChoiceType. Additionally the "preferred_choices" option was updated to accept callables and property paths. The "choices_as_values" option will be removed in Symfony 3.0, where the choices will be passed in the values of the "choices" option by default. The reason for that is that, right now, choices are limited to strings and integers (i.e. valid array keys). When we flip the array, we remove that limitation. Since choice labels are always strings, we can also always use them as array keys: ```php // Not possible currently, but possible with "flip_choices" $builder->add('attending', 'choice', array( 'choices' => array( 'Yes' => true, 'No' => false, 'Maybe' => null, ), 'choices_as_values' => true, )); ``` All the features described here obviously also apply to subtypes of "choice", such as "entity". **choice_label** Returns the label for each choice. Can be a callable (which receives the choice as first and the key of the "choices" array as second argument) or a property path. If `null`, the keys of the "choices" array are used as labels. ```php // callable $builder->add('attending', 'choice', array( 'choices' => array( 'yes' => true, 'no' => false, 'maybe' => null, ), 'choices_as_values' => true, 'choice_label' => function ($choice, $key) { return 'form.choice.'.$key; }, )); // property path $builder->add('attending', 'choice', array( 'choices' => array( Status::getInstance(Status::YES), Status::getInstance(Status::NO), Status::getInstance(Status::MAYBE), ), 'choices_as_values' => true, 'choice_label' => 'displayName', )); ``` **choice_name** Returns the form name for each choice. That name is used as name of the checkbox/radio form for this choice. It is also used as index of the choice views in the template. Can be a callable (like for "choice_label") or a property path. The generated names must be valid form names, i.e. contain alpha-numeric symbols, underscores, hyphens and colons only. They must start with an alpha-numeric symbol or an underscore. If `null`, an incrementing integer is used as name. ```php // callable $builder->add('attending', 'choice', array( 'choices' => array( 'Yes' => true, 'No' => false, 'Maybe' => null, ), 'choices_as_values' => true, 'choice_name' => function ($choice, $key) { // use the labels as names return strtolower($key); }, )); // property path $builder->add('attending', 'choice', array( 'choices' => array( 'Yes' => Status::getInstance(Status::YES), 'No' => Status::getInstance(Status::NO), 'Maybe' => Status::getInstance(Status::MAYBE), ), 'choices_as_values' => true, 'choice_name' => 'value', )); ``` **choice_value** Returns the string value for each choice. This value is displayed in the "value" attributes and submitted in the POST/PUT requests. Can be a callable (like for "choice_label") or a property path. If `null`, an incrementing integer is used as value. ```php // callable $builder->add('attending', 'choice', array( 'choices' => array( 'Yes' => true, 'No' => false, 'Maybe' => null, ), 'choices_as_values' => true, 'choice_value' => function ($choice, $key) { if (null === $choice) { return 'null'; } if (true === $choice) { return 'true'; } return 'false'; }, )); // property path $builder->add('attending', 'choice', array( 'choices' => array( 'Yes' => Status::getInstance(Status::YES), 'No' => Status::getInstance(Status::NO), 'Maybe' => Status::getInstance(Status::MAYBE), ), 'choices_as_values' => true, 'choice_value' => 'value', )); ``` **choice_attr** Returns the additional HTML attributes for choices. Can be an array, a callable (like for "choice_label") or a property path. If an array, the key of the "choices" array must be used as keys. ```php // array $builder->add('attending', 'choice', array( 'choices' => array( 'Yes' => true, 'No' => false, 'Maybe' => null, ), 'choices_as_values' => true, 'choice_attr' => array( 'Maybe' => array('class' => 'greyed-out'), ), )); // callable $builder->add('attending', 'choice', array( 'choices' => array( 'Yes' => true, 'No' => false, 'Maybe' => null, ), 'choices_as_values' => true, 'choice_attr' => function ($choice, $key) { if (null === $choice) { return array('class' => 'greyed-out'); } }, )); // property path $builder->add('attending', 'choice', array( 'choices' => array( 'Yes' => Status::getInstance(Status::YES), 'No' => Status::getInstance(Status::NO), 'Maybe' => Status::getInstance(Status::MAYBE), ), 'choices_as_values' => true, 'choice_value' => 'htmlAttributes', )); ``` **group_by** Returns the grouping used for the choices. Can be an array/Traversable, a callable (like for "choice_label") or a property path. The return values of the callable/property path are used as group labels. If `null` is returned, a choice is not grouped. If `null`, the structure of the "choices" array is used to construct the groups. ```php // default $builder->add('attending', 'choice', array( 'choices' => array( 'Decided' => array( 'Yes' => true, 'No' => false, ), 'Undecided' => array( 'Maybe' => null, ), ), 'choices_as_values' => true, )); // callable $builder->add('attending', 'choice', array( 'choices' => array( 'Yes' => true, 'No' => false, 'Maybe' => null, ), 'choices_as_values' => true, 'group_by' => function ($choice, $key) { if (null === $choice) { return 'Undecided'; } return 'Decided'; }, )); // property path $builder->add('attending', 'choice', array( 'choices' => array( 'Yes' => Status::getInstance(Status::YES), 'No' => Status::getInstance(Status::NO), 'Maybe' => Status::getInstance(Status::MAYBE), ), 'choices_as_values' => true, 'group_by' => 'type', )); ``` **preferred_choices** Returns the preferred choices. Can be an array/Traversable, a callable (like for "choice_label") or a property path. ```php // array $builder->add('attending', 'choice', array( 'choices' => array( 'Yes' => true, 'No' => false, 'Maybe' => null, ), 'choices_as_values' => true, 'preferred_choices' => array(true), )); // callable $builder->add('attending', 'choice', array( 'choices' => array( 'Yes' => true, 'No' => false, 'Maybe' => null, ), 'choices_as_values' => true, 'preferred_choices' => function ($choice, $key) { return true === $choice; }, )); // property path $builder->add('attending', 'choice', array( 'choices' => array( 'Yes' => Status::getInstance(Status::YES), 'No' => Status::getInstance(Status::NO), 'Maybe' => Status::getInstance(Status::MAYBE), ), 'choices_as_values' => true, 'preferred_choices' => 'preferred', )); ``` **Technical Changes** To properly implement all this, the old `ChoiceListInterface` class was deprecated and replaced by a new, slimmer one. The creation of choice views is now separated from choice lists. Hence a lot of logic is not executed anymore when processing (but not displaying) a form. Internally, a `ChoiceListFactoryInterface` implementation is used to construct choice lists and choice views. Two decorators exist for this class: * `CachingFactoryDecorator`: caches choice lists/views so that multiple fields displaying the same choices (e.g. in collection fields) use the same choice list/view * `PropertyAccessDecorator`: adds support for property paths to a factory **BC Breaks** The option "choice_list" of ChoiceType now contains a `Symfony\Component\Form\ChoiceList\ChoiceListInterface` instance, which is a super-type of the deprecated `ChoiceListInterface`. **Todos** - [ ] Adapt CHANGELOGs - [ ] Adapt UPGRADE files - [ ] symfony/symfony-docs issue/PR Commits ------- 94d18e9 [Form] Fixed CS 7e0960d [Form] Fixed failing layout tests 1d89922 [Form] Fixed tests using legacy functionality d6179c8 [Form] Fixed PR comments 26eba76 [Form] Fixed regression: Choices are compared by their values if a value callback is given a289deb [Form] Fixed new ArrayChoiceList to compare choices by their values, if enabled e6739bf [DoctrineBridge] DoctrineType now respects the "query_builder" option when caching the choice loader 3846b37 [DoctrineBridge] Fixed: don't cache choice lists if query builders are constructed dynamically 03efce1 [Form] Refactored choice lists to support dynamic label, value, index and attribute generation
Thanks for your awesomeness! Thanks again. |
@Kharestani Better create a new issue if you do not want your feature request to get lost (but please check first that there isn't already a similar issue). |
@Kharestani see #16834 |
@HeahDude |
@webmozart What happened to choice_attr? |
@ABM-Dan What do you mean? |
@xabbuh I couldn't find any documentation for EntityType's choice_attr. After some investigating, it turns out it's only in the docs for 2.7, with an example that suits ChoiceType, but is a bit lackluster when it comes to EntityType (for instance, mentioning that $val in the example is an actual Entity, is definetly relevant). |
@ABM-Dan Can you please open a new issue on the docs repository if you think we can improve the documentation somehow? |
Will do at a later date. |
@xabbuh what about symfony/symfony-docs#6260 ? @ABM-Dan be careful if you preset I've planned to open an issue in the docs for it as type hinting in the best practices, but haven't done it yet. Ok my bad, there is no type hint yet, I read it here symfony/symfony-docs#6144. There is some work about that, I'll try to help this week-end. |
Nice! |
Depends on: #3879, #3968 (
OptionsParser::replaceDefaults
is required)I want to add three options to the ChoiceType that abstract functionality of EntityType for use in all choice fields. See also #3479.
The new options are:
"choice_labels"
If given, the "choices" option is interpreted as storing the choices in the values instead of the keys. This way, choices can be created that select between values that are not allowed to be passed as array keys (any non-integer or non-string value).
"choice_labels" can be:
"choice_values"
Like "choice_labels", but stores/generates the values that are stored in the "value" attributes of the generated HTML. Values must be strings and unique (i.e. no two choices must have the same value).
By default, the choices are used as values, if possible. If not possible, integer values are generated.
Like "choice_labels", this option can be an array, a callable or a string (if choices are objects).
"choice_attr"
Like choice_values.
"group_by"
The "group_by" option stores a callable that returns the choice group for each choice or a property path if the choices are objects.
"preferred_choices"
The "preferred_choices" option allows to pass the values of the preferred choices as array, a callable or a property path if the choices are objects.
Poll
If the "choices" option is given as hierarchical array sorting each choice into a choice group, there are two possible ways for associating choices with their labels/values. Which one do you prefer?
A. Duplicate the hierarchy
B. Array-index based
Please name your preferred option in the comments.
The text was updated successfully, but these errors were encountered: