I have an entity which is on the inverse side of a three one-to-one mappings. Entity FittingStep
is mapped by FittingStepSingleValue
, etc. FittingStep
has a field fittingStepType
which identifies which one of the three entities the FittingStep
should look for. I want to embed that object in the FittingStep
edit form.
I have defined forms as services for each of the subforms:
services:
ihear.form.fitting_step_single_value:
class: Ihear\FittingBundle\Form\FittingStepSingleValueType
arguments: [@security.context]
tags:
-
name: form.type
alias: ihear_fittingbundle_fittingstepsinglevaluetype
ihear.form.fitting_step_double_value:
class: Ihear\FittingBundle\Form\FittingStepDoubleValueType
arguments: [@security.context]
tags:
-
name: form.type
alias: ihear_fittingbundle_fittingstepdoublevaluetype
ihear.form.fitting_step_option:
class: Ihear\FittingBundle\Form\FittingStepOptionType
arguments: [@security.context]
tags:
-
name: form.type
alias: ihear_fittingbundle_fittingstepoptiontype
These service classes look like this (pretty basic)
class FittingStepSingleValueType extends AbstractType
{
private $securityContext;
public function __construct(SecurityContext $securityContext)
{
$this->securityContext = $securityContext;
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('max1')
->add('description1')
->add('fittingStep', 'hidden')
;
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Ihear\FittingBundle\Entity\FittingStepSingleValue'
));
}
public function getName()
{
return 'ihear_fittingbundle_fittingstepsinglevaluetype';
}
}
My Form uses an EventListener on PRE_SET_DATA to add the appropriate embedded form field:
class FittingStepType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('name')
->add('abbreviation')
->add('description')
->add('fittingStepType',
'choice',
['choices' => ['SingleValue' => 'SingleValue',
'DoubleValue' => 'DoubleValue',
'Option' => 'Option'],
'empty_value' => 'select one please'])
;
$formModifier = function(FormInterface $form, $fittingStepType) {
switch ($fittingStepType) {
case 'SingleValue':
$form->add('fittingStepSingleValue',
'ihear_fittingbundle_fittingstepsinglevaluetype');
if ($form->has('fittingStepDoubleValue'))
$form->remove('fittingStepDoubleValue');
if ($form->has('fittingStepOption'))
$form->remove('fittingStepOption');
break;
case 'DoubleValue':
$form->add('fittingStepDoubleValue',
'ihear_fittingbundle_fittingstepdoublevaluetype');
if ($form->has('fittingStepSingleValue'))
$form->remove('fittingStepSingleValue');
if ($form->has('fittingStepOption'))
$form->remove('fittingStepOption');
break;
case 'Option':
$form->add('fittingStepOption',
'ihear_fittingbundle_fittingstepoptiontype');
if ($form->has('fittingStepSingleValue'))
$form->remove('fittingStepSingleValue');
if ($form->has('fittingStepDoubleValue'))
$form->remove('fittingStepDoubleValue');
break;
}
};
$builder->addEventListener(
FormEvents::PRE_SET_DATA,
function(FormEvent $event) use ($formModifier) {
$form = $event->getForm();
// this is the FittingStep
$data = $event->getData();
// this is the Entity that contains the value(s)
// i.e. FittingStepSingleValue
$fittingStepType = $data->getFittingStepType();
switch ($fittingStepType) {
case 'SingleValue':
$formModifier($form, $fittingStepType);
break;
case 'DoubleValue':
$formModifier($form, $fittingStepType);
break;
case 'Option':
$formModifier($form, $fittingStepType);
break;
}
}
);
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Ihear\FittingBundle\Entity\FittingStep'
));
}
public function getName()
{
return 'ihear_fittingbundle_fittingsteptype';
}
}
My controller for the edit action:
/**
* Displays a form to edit an existing FittingStep entity.
*
* @Route("/{id}/edit", name="admin_fittingstep_edit")
* @Method("GET")
* @Template()
*/
public function editAction($id)
{
$em = $this->getDoctrine()->getManager();
$entity = $em->getRepository('IhearFittingBundle:FittingStep')->find($id);
if (!$entity) {
throw $this->createNotFoundException(
'Unable to find FittingStep entity.');
}
$editForm = $this->createForm(new FittingStepType(), $entity);
$deleteForm = $this->createDeleteForm($id);
return array(
'entity' => $entity,
'edit_form' => $editForm->createView(),
'delete_form' => $deleteForm->createView(),
);
}
When I try to load the form, I get an exception:
The form's view data is expected to be of type scalar, array or an instance of \ArrayAccess, but is an instance of class Ihear\FittingBundle\Entity\FittingStep. You can avoid this error by setting the "data_class" option to "Ihear\FittingBundle\Entity\FittingStep" or by adding a view transformer that transforms an instance of class Ihear\FittingBundle\Entity\FittingStep to scalar, array or an instance of \ArrayAccess.
What am I doing wrong? It seems like there is some disconnect between the object mapping between the entities and the mapping on the form. As a side note, I'm using the $formModifier
closure successfully in my create form, so it works fine when creating a new entity with the embedded form.
You have to update the fittinStep from hidden to Collection
->add('fittingStep', 'hidden')
Collection doc: http://symfony.com/doc/current/reference/forms/types/collection.html
->add('fittingStep', 'collection', array('type'=>new FittingStepType(), 'label'=>false))
I figured out the problem. The relationship between the entities was referenced by the embedded entity, so I had added a hidden field for the parent entity to the embedded form. This little rascal did not cause any trouble with the creation form, but caused the error above when I embedded the form to edit an existing entity. Thanks @albert for pointing me to that line of code!
class FittingStepSingleValueType extends AbstractType
{
private $securityContext;
public function __construct(SecurityContext $securityContext)
{
$this->securityContext = $securityContext;
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('max1')
->add('description1')
// this line was the culprit
//->add('fittingStep', 'hidden')
;
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Ihear\FittingBundle\Entity\FittingStepSingleValue'
));
}
public function getName()
{
return 'ihear_fittingbundle_fittingstepsinglevaluetype';
}
}
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