Skip to content

[Form] Example of customizing EnumType labels #17149

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 1 commit into from
Dec 13, 2022
Merged
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
34 changes: 34 additions & 0 deletions reference/forms/types/enum.rst
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,40 @@ This will display a ``<select>`` tag with the three possible values defined in
the ``TextAlign`` enum. Use the `expanded`_ and `multiple`_ options to display
these values as ``<input type="checkbox">`` or ``<input type="radio">``.

Since the label displayed in the ``<select>`` options is the enum name, you might sometimes
want more flexibility as PHP strongly restricts the usable characters for those.
You could do this by implementing a function in your enum class which returns a label
or even a translation string for each possible enum::

// src/Config/TextAlign.php
namespace App\Config;

enum TextAlign: string
{
case Left = 'Left/Start aligned';
case Center = 'Center/Middle aligned';
case Right = 'Right/End aligned';

public function label(): string
Copy link
Member

Choose a reason for hiding this comment

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

Adding this method to the enum class starts mixing data and presentation which I would not advise. Let's configure the choice label using a closure instead (see comment below).

{
return match ($this) {
self::Left => 'text_align.left.label',
self::Center => 'text_align.center.label',
self::Right => 'text_align.right.label',
};
}
}

You can then use the ``choice_label`` option of ``EnumType`` with a function that
returns the label::

->add('textAlign', EnumType::class, [
'class' => TextAlign::class,
'choice_label' => static function (TextAlign $choice): string {
return $choice->label();
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
return $choice->label();
return match ($choice) {
TextAlign::Left => 'text_align.left.label',
TextAlign::Center => 'text_align.center.label',
TextAlign::Right => 'text_align.right.label',
};

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This would work if you only needed to display the labels in your form, but if you also need them to appear anywhere else in your application I think adding a method in the enum class is a better solution. I don't think adding presentation methods in enum classes is a bad practice.

Copy link
Member

Choose a reason for hiding this comment

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

If I had different places where I needed the label, I would go with a separate class that maps enum instances to labels to be honest.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That might be a way to do it but maybe a bit overkill. Even the PHP documentation suggests adding a label method in enum classes in their examples : https://www.php.net/manual/fr/language.enumerations.examples.php

Copy link
Member

Choose a reason for hiding this comment

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

I don't know what to say here. I think both of you gave good reasons to do opposite things.

Let's ask @wouterj and @OskarStark. Thanks!

Copy link
Member

@wouterj wouterj Oct 6, 2022

Choose a reason for hiding this comment

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

I agree with @xabbuh here. I understand your point, @robinbrisa, and it is also proven by the official PHP docs.

However, I would say that an enum can ship a generic presentation layer. The labels provided in this example indirectly couple the Enum's presentation layer to the Symfony Translation component - without it, the labels render worse human readable than their default string representation from the BackedEnum.
In that case, I would favor to keep the enum decoupled from Symfony Translation and handle the presentation in the layer that is responsible for mapping between Symfony Translation and the Enum, which is this function :)

Copy link
Contributor

Choose a reason for hiding this comment

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

Another way to see it would be to have MyEnum::getLabel() and/or MyEnum::getTranslationKey() to make it more reusable with respect for different layers.
However for the docs I agree this callable is a good way to go.

},
])
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
])
]);


Field Options
-------------

Expand Down