So, going into day 5 of troubleshooting this one and still can't work it out. I've even created a special form just for troubleshooting this, still haven't been able to make any headway. Basic issue: I have a Symfony2 form built via FormBuilderInterface using Form Modifiers to dynamically populate a field based on user selection of the first entity in the form. The form also has 3 collections in it (which represent One-To-One associations with the form's main class).
No matter what I do, on if ($form->isValid())
I get the all-too-familiar and fun "ERROR: This form should not contain extra fields." error on form submission (3 times, 1 for each collection).
Notable and probably related to the fix: If I remove the collection entities from the form, it validates correctly. With any of the collections in the form, or any combination of 2 of the 3 collections (I've tried all), it throws the "This form should not contain extra fields" error.
Here is the code for the form type:
class ThisDataClassType extends AbstractType
{
protected $user_id;
public function __construct($user_id) {
$this->user_id = $user_id;
}
/**
* @param FormBuilderInterface $builder
* @param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$user_id = $this->user_id;
$builder->add('firstChoice', 'entity', array(
'class' => 'MyBundle:FirstChoice',
'query_builder' => function(firstChoiceRepository $repository) use ($user_id) {
return $repository->createQueryBuilder('f')
->where('f.user = ?1')
->andWhere('f.isActive = 1')
->setParameter(1, $user_id);
},
'property' => 'firstChoice_name',
'required' => true,
'mapped' => false,
'label' => 'block.firstChoice.name'
))
->add('sub_block_name','text', array(
'label' => 'subblock.block.name',
'max_length' => 50,
'required' => true,
'attr' => array(
'placeholder' => 'subblock.phv.name',
'pattern' => 'alpha_numeric'
)
))
// ... bunch of other standard form types (text, etc.) ... //
->add('subdata1', 'collection', array(
'type' => new SubData1Type()
))
->add('subdata2', 'collection', array(
'type' => new SubData2Type()
))
->add('subdata3', 'collection', array(
'type' => new SubData3Type()
));
$formModifier = function(FormInterface $form, $firstChoice_id) {
$form->add('secondChoice', 'entity', array(
'class' => 'MyBundle:SecondChoice',
'query_builder' => function(secondChoiceRepository $S_repository) use ($firstChoice_id) {
return $S_repository->createQueryBuilder('s')
->where('s.firstChoice_id = ?1')
->andWhere('s.isActive = 1')
->setParameter(1, $firstChoice_id);
},
'property' => 'secondChoice_name',
'label' => 'block.secondChoice.name'
));
};
$builder->addEventListener(
FormEvents::PRE_SET_DATA,
function(FormEvent $event) use ($formModifier) {
$data = $event->getData()->getId();
$formModifier($event->getForm(), $data);
}
);
$builder->get('firstChoice')->addEventListener(
FormEvents::POST_SUBMIT,
function(FormEvent $event) use ($formModifier) {
$data = $event->getForm()->getData();
$formModifier($event->getForm()->getParent(), $data->getId());
}
);
$builder->setMethod('POST');
}
/**
* @param OptionsResolverInterface $resolver
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'MyBundle\Entity\ThisDataClass',
'user_id' => null,
'translation_domain' => 'block',
'cascade_validation' => true
));
}
/**
* @return string
*/
public function getName()
{
return 'ThisDataForm';
}
}
...and the basic controller (just used for Testing at the moment):
public function TestFormAction(Request $request, $whichSize)
{
$user_id = $this->getUser()->getId();
$success = 'Not submitted';
$dataclass = new Entity\ThisDataClass();
$subdata1 = new Entity\SubData1();
$subdata2 = new Entity\SubData2();
$subdata3 = new Entity\SubData3();
if (!$request->isMethod('POST')) {
$dataclass->getSubData1()->add($subdata1);
$dataclass->getSubData2()->add($subdata2);
$dataclass->getSubData3()->add($subdata3);
}
$form = $this->createForm(new Form\ThisDataClassType($user_id), $dataclass, array(
'action' => $this->generateUrl('my_custom_test_route', array('whichSize' => $whichSize)),
'user_id' => $user_id
));
$form->handleRequest($request);
if ($form->isValid()) {
$success = 'Success!!';
return $this->render('MyBundle:DataClassTestForm.html.twig', array('dataClassTestForm' => $form->createView(), 'whichSize' => $whichSize, 'success' => $success));
} else {
$success = $form->getErrorsAsString();
}
return $this->render('MyBundle:DataClassTestForm.html.twig', array('dataClassTestForm' => $form->createView(), 'whichSize' => $whichSize, 'success' => $success));
}
To mention a couple of other important points:
'mapped' => false
set, and
the CSRF _token, but I get the error whether I explicitly enable the CSRF _token
or not, and again the error disappears when I remove the collections)Greatly appreciate anyone's insight on what it is I'm missing.
After taking a day and a half off and getting away, the answer popped when I sat down and is quite obvious. The controller as written only adds the subdata classes to the form when it is first created (ie when no submit/POST event has occurred). Hence the form created AFTER the submit event has all of the post data, but the classes are not associated with it. The controller has now been rewritten as below and the form now validates as expected:
public function TestFormAction(Request $request, $whichSize)
{
$user_id = $this->getUser()->getId();
$success = 'Not submitted';
$dataclass = new Entity\ThisDataClass();
$subdata1 = new Entity\SubData1();
$subdata2 = new Entity\SubData2();
$subdata3 = new Entity\SubData3();
$dataclass->getSubData1()->add($subdata1);
$dataclass->getSubData2()->add($subdata2);
$dataclass->getSubData3()->add($subdata3);
$form = $this->createForm(new Form\ThisDataClassType($user_id), $dataclass, array(
'action' => $this->generateUrl('my_custom_test_route', array('whichSize' => $whichSize)),
'user_id' => $user_id
));
$form->handleRequest($request);
if ($form->isValid()) {
$success = 'Success!!';
} else {
$success = 'Invalid!!';
}
} // RESULT::Success!!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With