My problem is not that simple I will try to summarize as best as I can.
Let’s say I have an entity Vehicle which is related to an entity Engine. Engine entity is an abstract type which is implemented by 2 classes : ElectricEngine and GasEngine.
I want to create a form for Vehicle which will create a vehicle and his engine type, but I cannot add a CustomType field with Engine entity binded because it is an abstract entity. So I need to dynamically add a CustomType field with ElectricEngine or GasEngine. Here is VehicleType.php :
// AppFormVechicleType::buildForm $builder ->add('wheels', IntegerType::class) ->add('type', ChoiceType::class, [ 'choices' => [ 'ElectricEngine' => 1, 'GasEngine' => 2 ], 'mapped' => false ])
So when the user submits this form, I want to cancel the submit and add my ElectricEngineType or my GasEngineType field depending which one my user selected.
For now, here is my solution : I’m using POST_SUBMIT Event when building VehicleForm. I’m watching which type the user selected, and I’m adding the field that match. And then I add an error to cancel submitting so that the form is rendered once again, but with the new field.
// AppFormVehicleTypeForm::buildForm // After building form ... $builder->get('type')->addEventListener(FormEvents::POST_SUBMIT, function(FormEvent $event){ $form = $event->getForm()->getParent(); if($form->get('type')->getData() != null) { switch ($form->get('type')->getData()) { case 1: $form->add('engine', ElectrineEngine::class); $form->remove('type'); break; case 2: $form->add('engine', GasEngine::class); $form->remove('type'); break; default: // Do Nothing } // Adding Error so the form is not submitted $form->addError(new FormError('Adding engine field')); } });
By adding an error, the form is no longer valid so it will be rendered again. If anyone has the same problem as me, this is a solution.
But I don’t think it is the best one because I add an error to the form, and there is no error. I would like to have a better way to implement this but I don’t know how to do it.
I’ve considered using https://github.com/craue/CraueFormFlowBundle for using multiple steps, but this would add Electric Engine, and Gas Engine step, and we would have to skip one. I don’t think this is the best idea because in my real project (I’m not developping nothing with vehicle and engines of course) I have 3 children of the abstract Entity and more will come. Maybe this bundle is the best idea but I’m not pretty sure :/
Advertisement
Answer
I’ve found a way better solution. For people having the same issue this is for you.
Instead of doing one generic form, and adapting it, the best way is to have many forms and to add the common part each time. Yes I am dumb, I didnt think of it.
With the vehicle example : we would have a VehicleForm with generic fields. And here would be the ElectricEngineForm :
// AppFormElectricEngineForm public function buildForm(FormBuilderInterface $builder, array $options) { $genericType = new VehicleType(); $genericType->buildForm($builder, $options); $builder->add('engine', ElectricEngineType::class); } public function configureOptions(OptionsResolver $resolver) { $resolver->setDefaults([ 'data_class' => Vehicle::class ]); }
ElectricEngineType is the form that contains specifities of an ElectricEngine. You also have to create GasEngineForm and GasEngineType. Then you render the one you want depending the route the user has taken.
If the user is in /vehicle/add/electric, then we render ElectricEngineForm, else if he is in /vehicle/add/gas, then we render GasEngineForm.
If you want you can build the generic form with other methods I think. Maybe a static method or else I don’t know.