Alsatian67 / FormBundle

Bunch of FormTypes extending some basic Symfony PHP framework FormTypes. ExtensibleTypes makes it possible to allow extra choices by Symfony ChoiceTypes (ChoiceType, EntityType and DocumentType). This can help to make it work with the Select2 jQuery pluggin.
MIT License
14 stars 6 forks source link

The choice "464" does not exist or is not unique #8

Open artscorestudio opened 7 years ago

artscorestudio commented 7 years ago

Hi, I do not understand why but I still have the following error: "The choice "464" does not exist or is not unique". By doing tests, I get into the "subscriber", the "choices" array is populated but the error is still there.

In the profiler, I have the following information after submission :

Default Data :
    Model Format : same as normalized format
    Normalized format : null
    View format : 464

Submitted Data :
    View format : 464
    Normalized format : null
    Model Format : same as normalized format

Passed options :
    choices : [0 => Object(Proxies\......MyEntity)]

Can you help me ?

Alsatian67 commented 7 years ago

Which Type (Choice/Entity/Document) are you using ? How do you use it ?

artscorestudio commented 7 years ago

Hi,

I use an Entity.

In my form :

$builder->add('arriveIn', ExtensibleEntityType::class, array(
            'choices' => array(), // Prevent default EntityType behavior (load all entities)
            'label' => 'Service',
            'route' => 'ref_service_search_all',
            'class' => 'AppBundle:Service',
            'choice_label' => 'name',
        ));

The subscriber (Alsatian\FormBundle\Form\Extensions\ExtensibleSubscriber) do his job on PRE_SET_DATA and PRE_SUBMIT :

// [...]
$form->add($childName, $type['originalType'], $options);
if ( $childName == 'arriveIn' ) { // For testing choices are populated - it's OK.
    dump($form->get($childName)->getConfig()->getOptions()['choices']);
}

Note : I have 5 fields in the form with the ExtensibleEntityType. But I don't think it's the problem.

artscorestudio commented 7 years ago

When I dig in Form mechanism, I found this :

  1. EntityType use \Symfony\Component\Form\Extension\Core\DataTransformer\ChoiceToValueTransformer on this types of fields.
  2. ChoiceToValueTransformer use \Symfony\Component\Form\ChoiceList\ArrayChoiceList::getChoicesForValues for load choice list
  3. getChoicesForValues() use $this->choices who is generated with the parameter in form type options.
  4. form type options is empty array() but your subscriber fill it normaly... no ?
Alsatian67 commented 7 years ago

First of all, try to remove choices' => array() by your form definition.

It is already set here

Where is your exception thrown ? From the subscriber ?

if yes : on pre_set_data, or on pre_submit ?

Is 674 the id of a submitted AppBundle:Service entity ?

artscorestudio commented 7 years ago

Both of them. I tested to inject a Service entity mapped in arriveIn field. I pass through the subscriber, it does its job but nothing... no option tag... it's like the $form in subscriber it's ont the same as the $form create in controller.

Otherwhise, the exception is thrown in transformer on PRESUBMIT. Logic, because the list is empty.

Alsatian67 commented 7 years ago

Did you try to remove your choices => array() ?

artscorestudio commented 7 years ago

Yes

artscorestudio commented 7 years ago

I will try to do the same thing again in a clean symfony version. For information I am under symfony 2.8.*. I will make you a return.

Alsatian67 commented 7 years ago

I wrote this bundle using the 2.8 version ...

My subscriber works with a priority of -50 : https://github.com/Alsatian67/FormBundle/blob/master/Form/Extensions/ExtensibleSubscriber.php#L29

May be you have some other listener acting after my PRE_SUBMIT event ? I can't find the official doc about built in listeners priority anymore :(

artscorestudio commented 7 years ago

Hi,

I tested your bundle on a fresh Symfony 2.8.18... and I have the same behavior. Maybe I missed something...

I started by the view :

{% extends 'base.html.twig' %}
{% block body %}
    {{ form_start(form) }}
    {{ form_row(form) }}
    <input type="submit" value="Ajouter" />
    {{ form_end(form) }}
{% endblock %}

The Form :

class MyFormType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder->add('service', ExtensibleEntityType::class, array(
            'choice_label'=>'name',
            'class' => 'AppBundle\Entity\Service',
            'placeholder' => 'Veuillez sélectionner un service'
        ));
    }

    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults(array(
            'data_class' => 'AppBundle\Entity\MyEntity'
        ));
    }

    public function getBlockPrefix()
    {
        return 'my_form';
    }

    public function getName()
    {
        return $this->getBlockPrefix();
    }
}

The controller :

public function indexAction(Request $request)
{
        $myEntity = new MyEntity();
        $form = $this->createForm(MyFormType::class, $myEntity);
        $form->handleRequest($request);

        if ( $form->isSubmitted() && $form->isValid() ) {
            $myEntity = $form->getData();
        }

        return $this->render('AppBundle:default:index.html.twig', array(
            'form' => $form->createView()
        ));
}

MyEntity entity :

class MyEntity
{
    /**
     * @ORM\Id
     * @ORM\Column(type="integer")
     * @ORM\GeneratedValue(strategy="AUTO")
     * 
     * @var integer
     */
    private $id;

    /**
     * @ORM\ManyToOne(targetEntity="Service")
     * @ORM\JoinColumn(nullable=false)
     * @Assert\NotNull()
     * 
     * @var \AppBUndle\Entity\Service
     */
    private $service;
}

Service entity :

class Service
{
    /**
     * @ORM\Id
     * @ORM\Column(type="integer")
     * @ORM\GeneratedValue(strategy="AUTO")
     *
     * @var integer
     */
    private $id;

    /**
     * @ORM\Column(type="string", nullable=false)
     * @Assert\NotNull()
     * 
     * @var string
     */
    private $name;
}
Alsatian67 commented 7 years ago

Et si on parlait français, ce serait peut-être plus simple ?

En fait je sais pas trop d'où vient ton problème. Il faut que je teste deux trois trucs pour voir ce que ça peut être. C'est aussi possible que Symfony a changé quelque chose depuis aout 2016 (y compris sur Symfony 2.8) en corrigeant un bug ...

J'ai vu que plusieurs commits ont changé ce qui touche à l'EntityType que je surcharge ...

Je n'utilise plus du tout symfony depuis plusieurs mois et je ne pas si mon bundle marche encore sur mon application, je vais m'y replonger pour y répondre, mais j'ai pas trop le temps avant plusieurs jours.

Ce que tu peux faire pour débuguer :

Voilà ce que tu peux faire en attendant ...

Je teste dès que j'ai le temps

artscorestudio commented 7 years ago

J'ai fais les tests sur tous les événements, et la modification est bien prise en compte pratout. Lorsque je reviens sur mon formulaire après soumission, mon option injecté pendant le PRE_SUBMIT est bien présente dans la rubrique "passed options".

Je crois que j'ai levé un loup, le noeud du problème est que le transformer dans son reverse_transform fait appel au loader ArrayChocieList qui pour contrôler les options fait :

foreach ($values as $i => $givenValue) {
            if (array_key_exists($givenValue, $this->choices)) {
                $choices[$i] = $this->choices[$givenValue];
            }
        }

Le $this->choices fait référence à la liste des choix que l'on donne dans configureOptions() du FormType. Sauf qu'à l'initialisation, il n'y en a pas... d'où l'erreur.

Ce qui n'est pas normal, c'est qu'on la modifie cette liste via PRE_SUBMIT et PRE_SET_DATA mais la modification n'est pas prise en compte à ce niveau là.

artscorestudio commented 7 years ago

J'ai avancé,

Lorsque j'édite une entité qui a un service de renseigné (entité Service mappé sur le champ service), le PRE_SET_DATA fonctionne si et seulement si je définie un buildView() de mon formulaire MyFormType.

Par contre, lorsque je soumet le formulaire, toujours rien.

Alsatian67 commented 7 years ago

Bizarre,

chez moi j'ai tout mis à jour, (sur symfony 3.2), j'ai tout re-testé et tout fonctionne toujours très bien. Dans mon app, mes formtypes n'ont pas de configuration particulière, juste le minimum requis.

Peut-tu me faire un genre de fork minimal qui reproduit le bug ?

artscorestudio commented 7 years ago

Je vais te mettre dans un dépôt le symfony 2.8 et ton bundle.

artscorestudio commented 7 years ago

capture

Je sais si cela va te parler mais voilà ce que me ressort la debug toolbar après soumission du formulaire.

Pasuvan commented 4 years ago

I have the same issue did you resolved above issue, if yes then can you explain?

Alsatian67 commented 4 years ago

I could not reproduce the same issue. It is till open.