Skip to content
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

Fix some issues in Choice field with PHP Enums #5620

Closed
wants to merge 1 commit into from

Conversation

javiereguiluz
Copy link
Collaborator

Fixes the issues reported in #4988.

@abozhinov
Copy link
Contributor

When you plan to merge it?

@abozhinov
Copy link
Contributor

abozhinov commented Feb 18, 2023

Hi,
my implementation is:

yield ChoiceField::new('weight.unit', new TranslatableMessage('weight.unit.label'));
#[Embeddable]
final class Weight
{
    #[Column(type: 'float', nullable: false)]
    #[Assert\GreaterThanOrEqual(0)]
    #[Groups(['read', 'write'])]
    private float $value;

    #[Column(type: 'string', nullable: false, enumType: WeightUnit::class)]
    #[Groups(['read', 'write'])]
    private WeightUnit $unit;

The error:

#message: "An error has occurred resolving the options of the form "Symfony\Component\Form\Extension\Core\Type\EnumType": The required option "class" is missing."
  #code: 0
  #file: "[/var/www/html/dreamshop/vendor/symfony/form/ResolvedFormType.php](https://docker.mydreamshop.io/_profiler/open?file=vendor/symfony/form/ResolvedFormType.php&line=78#line78)"

Copy link
Contributor

@abozhinov abozhinov left a comment

Choose a reason for hiding this comment

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

On 42 line should set CLASS option -> $field->setFormTypeOption('class', $enumTypeClass);

Copy link
Contributor

@abozhinov abozhinov left a comment

Choose a reason for hiding this comment

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

Choices should be ENUM not STRING.

foreach ($choices as $choice) {
    $processedEnumChoices[$choice->name] = $choice;
}

@ksn135
Copy link
Contributor

ksn135 commented Feb 18, 2023

My work with Enum with the current 4.x branch looks like this:

Screenshot 2023-02-18 at 14 47 24

enum Nds: string
{
    case RATE_NONE = 'rate_none';
    case RATE_ZERO = 'rate_0';
    case RATE_10 = 'rate_10';
    case RATE_20 = 'rate_20';

    public static function choices(): array
    {
        return [
            self::RATE_NONE->getLabel() => self::RATE_NONE->name,
            self::RATE_ZERO->getLabel() => self::RATE_ZERO->name,
            self::RATE_10->getLabel() => self::RATE_10->name,
            self::RATE_20->getLabel() => self::RATE_20->name,
        ];
    }

    public function trans(TranslatorInterface $translator, string $locale = null): string
    {
        return $translator->trans($this->getLabel(), locale: $locale);
    }

    public function getLabel(): string
    {
        return 'admin_label.enum.nds.'.$this->value;
    }

    public function getRate(): int
    {
        return (int) (substr($this->value, 5));
    }
}
# messages.en.yaml
admin_label:
  enum:
    nds:
      rate_none: Without VAT
      rate_0: 0%
      rate_10: 10%
      rate_20: 20%
class MyEnumType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options): void
    {
        $builder->addModelTransformer(
            new CallbackTransformer(
                static fn (mixed $value): mixed => $value, // no convertion
                static fn (?string $value): ?\BackedEnum => call_user_func([$options['class'], 'tryFrom'], $value)
            )
        );
    }

    public function configureOptions(OptionsResolver $resolver): void
    {
        $resolver->setNormalizer('choice_label', static fn (Options $options, ?\Closure $value): \Closure => static fn ($choice): ?string => self::getTranslatableLabel($options, $choice)); 
        $resolver->setNormalizer('choice_value', static fn (Options $options, ?\Closure $value): \Closure => static fn ($choice): ?string => $choice instanceof \BackedEnum ? $choice->value : $choice);
        $resolver->setRequired('label_prefix');
        $resolver->setAllowedTypes('label_prefix', 'string');
        $resolver->setDefault('label_prefix', 'admin_label.enum.');
        $resolver->setRequired('class');
        $resolver->setAllowedTypes('class', 'string');

    }

    public static function getTranslatableLabel(Options $options, string $value) {
        return $options['label_prefix'] . lcfirst(self::classShortName($options['class'])) . '.' . $value;
    }

    public static function classShortName(string $class): string
    {
        return (false === $pos = strrpos($class, '\\')) ? $class : substr($class, $pos + 1);
    }

    public function getParent(): string
    {
        return EnumType::class;
    }
}
class DocCrudController extends AbstractCrudController
{
...
    public function configureFields(string $pageName): iterable
    {
      ...
        yield Fields\ChoiceField::new('nds', 'admin_label.doc.field.nds')->setRequired(true)
           ->setPermission(DocAccessVoter::SHOW_NDS)
            ->renderAsNativeWidget()
            ->addCssClass('col-md-6 col-xxl-5')
            ->setFormType(MyEnumType::class)
            ->setFormTypeOption('placeholder', false)
            ->setFormTypeOption('class', Nds::class)
            ->setChoices(in_array($pageName, [Crud::PAGE_INDEX, Crud::PAGE_DETAIL], true) ? Nds::choices() : Nds::cases())
        ;
       ...
     }
...
}

@abozhinov
Copy link
Contributor

This is my PR -> #5640

@ksn135
Copy link
Contributor

ksn135 commented Feb 18, 2023

#5641

@sylvaindeloux
Copy link

@javiereguiluz
Is it planned to be merged soon ?
#5641 is quite annoying :/

@javiereguiluz
Copy link
Collaborator Author

Closing in favor of #5740.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants