Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Fix ChoiceType terminology #6144

Copy link
Copy link
Closed
Closed
Copy link
@webmozart

Description

@webmozart
Issue body actions

Currently, the ChoiceType documentation is IMO not clearly worded. I know that readability is important, but since this type is quite flexible and complex, being precise is even more important. All code examples and descriptions should be updated to use the following terms:

  • choice: The model value of the choices array. E.g.: true, false, object (Category), ...
  • key: The key of the choices array.
  • value: The HTML value. The value is always a string. E.g.: '0', '1', 'yes', 'no', ... Values must be free of duplicates, since they are used to identify the corresponding choice when submitting the form.

This naming is important since those values are passed from one callable to another:

  1. The choice_value callable with signature function ($choice) receives the choice and returns the string value.
  2. The choice_label callable with signature function ($choice, $key, $value) receives the choice, the key and the value and returns a string label. The default value is function ($choice, $key, $value) => $key, i.e. the key of the choices array is used as label by default.
  3. The choice_name callable has the same signature as choice_label and outputs the form name used for checkboxes/radio buttons.
  4. The choice_attr callable has the same signature as choice_label and outputs the HTML attributes.

Furthermore, the choice_loader is not completely accurate:

The choice_loader can be used to only partially load the choices in cases where a fully-loaded list is not necessary. This is only needed in advanced cases and would replace the choices option.

I would rephrase this. The choice loader is used to load choices from a data source that can be queried with a query language, such as a database or a search engine. When the choice field is displayed, the full list is loaded, but when it is submitted, only the submitted value is looked up. A second benefit is that different fields that use the same ChoiceLoader instance use the same cached choice list, reducing N queries to 1.

Before/After code examples:

Before:

use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
// ...

$builder->add('attending', ChoiceType::class, array(
    'choices' => array(
        'yes' => true,
        'no' => false,
        'maybe' => null,
    ),
    'choices_as_values' => true,
    'choice_label' => function ($value, $key, $index) {
        if ($value == true) {
            return 'Definitely!';
        }
        return strtoupper($key);

        // or if you want to translate some key
        //return 'form.choice.'.$key;
    },
));

After:

use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
// ...

$builder->add('attending', ChoiceType::class, array(
    'choices' => array(
        'yes' => true,
        'no' => false,
        'maybe' => null,
    ),
    'choices_as_values' => true,
    'choice_label' => function ($choice, $key, $value) {
        return true === $choice ? 'Definitely!' : strtoupper($key);

        // or if you want to translate some key
        //return 'form.choice.'.$key;
    },
));

Before:

use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
// ...

$builder->add('attending', ChoiceType::class, array(
    'choices' => array(
        'Yes' => true,
        'No' => false,
        'Maybe' => null,
    ),
    'choices_as_values' => true,
    'choice_attr' => function($val, $key, $index) {
        // adds a class like attending_yes, attending_no, etc
        return ['class' => 'attending_'.strtolower($key)];
    },
));

After:

use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
// ...

$builder->add('attending', ChoiceType::class, array(
    'choices' => array(
        'Yes' => true,
        'No' => false,
        'Maybe' => null,
    ),
    'choices_as_values' => true,
    'choice_attr' => function($choice, $key, $value) {
        // adds a class like attending_yes, attending_no, etc
        return ['class' => 'attending_'.strtolower($key)];
    },
));

I also recommend to add type hints in the advanced example:

Before:

use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use AppBundle\Entity\Category;
// ...

$builder->add('category', ChoiceType::class, [
    'choices' => [
        new Category('Cat1'),
        new Category('Cat2'),
        new Category('Cat3'),
        new Category('Cat4'),
    ],
    'choices_as_values' => true,
    'choice_label' => function($category, $key, $index) {
        /** @var Category $category */
        return strtoupper($category->getName());
    },
    'choice_attr' => function($category, $key, $index) {
        return ['class' => 'category_'.strtolower($category->getName())];
    },
    'group_by' => function($category, $key, $index) {
        // randomly assign things into 2 groups
        return rand(0, 1) == 1 ? 'Group A' : 'Group B'
    },
    'preferred_choices' => function($category, $key, $index) {
        return $category->getName() == 'Cat2' || $category->getName() == 'Cat3';
    },
]);

After:

use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use AppBundle\Entity\Category;
// ...

$builder->add('category', ChoiceType::class, [
    'choices' => [
        new Category('Cat1'),
        new Category('Cat2'),
        new Category('Cat3'),
        new Category('Cat4'),
    ],
    'choices_as_values' => true,
    'choice_label' => function(Category $category, $key, $value) {
        return strtoupper($category->getName());
    },
    'choice_attr' => function(Category $category, $key, $value) {
        return ['class' => 'category_'.strtolower($category->getName())];
    },
    'group_by' => function(Category $category, $key, $value) {
        // randomly assign things into 2 groups
        return rand(0, 1) == 1 ? 'Group A' : 'Group B'
    },
    'preferred_choices' => function(Category $category, $key, $index) {
        return $category->getName() == 'Cat2' || $category->getName() == 'Cat3';
    },
]);

Metadata

Metadata

Assignees

No one assigned

    Labels

    DXFormactionableClear and specific issues ready for anyone to take them.Clear and specific issues ready for anyone to take them.hasPRA Pull Request has already been submitted for this issue.A Pull Request has already been submitted for this issue.⭐️ EU-FOSSA Hackathonhttps://symfony.com/blog/the-symfony-and-api-platform-hackathon-is-cominghttps://symfony.com/blog/the-symfony-and-api-platform-hackathon-is-coming

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions

      Morty Proxy This is a proxified and sanitized view of the page, visit original site.