symfony: Symfony : composer update 4.4.19 > 4.4.20 occures "The value array is not valid" when submitting form with entitytype.

Symfony version(s) affected: 4.4.2

Description After updating Symfony 4.4.19 to 4.4.20, some forms using Entitytype class - populated with json - give the following error when handling request :


The value array is not valid. | recipients | Caused by:                                                             
Symfony\Component\Form\Exception\TransformationFailedException {#6790 ▼
  -invalidMessage: null
  -invalidMessageParameters: []
  #message: "The choices "XXXX" do not exist in the choice list."
  #code: 0
  #file: "/project/vendor/symfony/form/Extension/Core/Type/ChoiceType.php"
  #line: 183
  trace: {▼
    /project/vendor/symfony/form/Extension/Core/Type/ChoiceType.php:183 {▶}
    /project/vendor/symfony/event-dispatcher/EventDispatcher.php:264 {▶}
    /project/vendor/symfony/event-dispatcher/EventDispatcher.php:239 {▶}
    /project/vendor/symfony/event-dispatcher/EventDispatcher.php:73 {▶}
    /project/vendor/symfony/event-dispatcher/ImmutableEventDispatcher.php:44 {▶}
    /project/vendor/symfony/form/Form.php:671 {▶}
    /project/vendor/symfony/form/Form.php:579 {▶}
    /project/vendor/symfony/form/Extension/HttpFoundation/HttpFoundationRequestHandler.php:109 {▶}
    /project/vendor/symfony/form/Form.php:493 {▶}
    ...

It works perfectly with SF 4.4.19.

Regards,

How to reproduce

  • Juste create a simple form with an empty choice list:
$form = $this->createFormBuilder()
  ->add('recipients', EntityType::class, [
      'class' => MyEntity::class,
      'translation_domain' => 'mytranslation_domain',
      'choice_label' => 'choice_label',
      'label' => 'em.texts.to',
      'multiple' => true,
      'mapped' => true,
      'choices' => $mychoices
]);
  • print in a twig using select2 lib

  • populate with a json request, make a choice and submit

Possible Solution Patching src/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php ?

Additional context

About this issue

  • Original URL
  • State: closed
  • Created 3 years ago
  • Comments: 38 (18 by maintainers)

Most upvoted comments

I’ve been having the same issue for a while, so my 2 cents for the discussion (and why I think this issue can be closed).

The behaviour before 4.4.20 was quite convenient as it helped to extend ChoiceType to build, for instance, an AJAX picker (what I was doing). In that way the #39659 bugfix, caused quite a mess in my project (and I would guess many - I suspect many issues, such as #41356, are related).

In the end, I believe it is correct for ChoiceType to validate that selected choices are indeed part of choices.
So, I think there’s nothing to fix here anymore, though I can see that making a more flexible ChoiceType would be great (maybe something that would also help with #39165, dynamic choices and options all the way).

So here’s a detailed workaround for whoever have the issue, to make a custom form type work :

  • your custom type should not extend ChoiceType anymore (but FormType or nothing at all) - doing so, you will have to handle a bit more logic (code snippet below)
  • first of all, you have to configure compound option (cf. snippet P1)
  • if you want to handle multiple selections, in buildView() you have to copy a bit of code (cf. snippet P2)
  • you need your own forme theme for your custom type (you can copy-paste the one from choice_widget for instance)
  • you have to pass multiple and placeholder options to the view (cf. snippet P3)
  • if you want to handle Doctrine ArrayCollection on top of arrays, you have to add MergeCollectionListener (cf. snippet P4)

With those tweaks, the rest of you custom code should work with Symfony 5.2.4+ (or 4.4.20+)

The snippet:

class MyCustomFormType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        // ...

        // P4: allow to handle ArrayCollections
        $builder->addEventSubscriber(new MergeCollectionListener(true, true));
    }

    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefault('compound', false); // P1: should always be "false"

        // P3: required to re-use same widget, passed to buildView()
        $resolver->setDefault('multiple', false);
        $resolver->setDefault('placeholder', null);

        // ...
    }

    public function buildView(FormView $view, FormInterface $form, array $options)
    {
        // P2: required to handle multiple, so values are not spread into multiple POST parameters
        if ($options['multiple']) {
            $view->vars['full_name'] .= '[]';
        }

        // P3: required for using the same widget
        $view->vars['multiple'] = $options['multiple'];
        $view->vars['placeholder'] = $options['placeholder'];
    }
}

that’s why I said the PRE_SUBMIT of the parent, where you can still replace the child with a new field using the ChoiceType (with different options.

@xabbuh Ok i do it soon