我在做什么
我正在从3个实体(表单/元素/选项)动态生成表单,其中包含表单元素和适用的选项(想想复选框/下拉框) .
设计Symfony表单时,您可以设置字段以匹配实体,实例在实例化时,使用该实体的值填充表单 .
但是,我希望填充表单的实体是用户提交实体的实体,但是要从表单实体构造的表单 . 所以我已经为自定义表单添加了功能,它从表单实体构建自己,但是然后期望提交实体 .
$formClass = new FormType();
$formClass->setDataModel($formEntity);
$this->createForm($formClass, $submission, array());
什么是/不起作用
我能够呈现一个checklist元素,这些值是从'form element option'实体中提取的,但我希望将用户选择保存到'submission element option'实体 .
对于他们做出的每个选择(选择每个'表单元素选项'),我想要保存“提交元素选项” .
checklist元素的选项来自FormElementOption实体,但用户提交的是SubmissionElementOption(引用FormElementOption) .
我正在使用此问题底部的变换器转换实体,但是当先前不存在提交时 - 它会尝试设置其父级 $subOption->setSubmissionElement($this->element);
但不能 .
在 $form->getData()
(将是以前保存的实体)的形式为null时,它将作为 $this->element
传递给变换器 .
如果为null,则新提交选项不能具有其父设置,因此会以选项 setSubmissionElement
不能为空的错误结束 .
从表单创建视图中您可以:
[Form] hasMany [Element] hasMany [Option]
形成:
id | name
1 | 'form 1'
元件:
id | name | form_id | type
1 | 'a' | 1 | 'checklist'
2 | 'b' | 1 | 'checklist'
3 | 'c' | 1 | 'text'
选项:
id | name | element_id
1 | 'a' | 1
2 | 'b' | 1
3 | 'c' | 1
4 | 'd' | 1
5 | 'a' | 2
从您提交的表单提交:
[Submission] hasMany [SubmissionElement] hasMany [SubmissionElementOption]
投稿方式:
id | user_id | form_id
1 | 1 | 1
提交要素:
id | submission_id | element_id
1 | 1 | 1
最后是Submission元素选项:
id | submission_element_id | option_id
1 | 1 | 1
2 | 1 | 2
3 | 1 | 4
表单是使用FormElement实体中的数据构建的,保存时应返回FormSubmissionElement实体 . 所以我在这里设置课程:
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => '\AppBundle\Entity\FormSubmissionElement'
));
}
为了呈现表单的元素,我将构造函数传递给FormElement .
FormChoiceType是一种简单类型,它扩展EntityType并将元素传递给数据Transformer .
该表单在pre_post_data事件侦听器中呈现该元素:
$form = $event->getForm();
/** @var \AppBundle\Entity\FormSubmissionElement $data */
$data = $event->getData();
...
$submission_options = $data !== null ? $data->getOptions() : null;
$submission_element = $data;
$type = new FormChoiceType($submission_element, $submission_options);
$form->add($element_name, $type, array(
'by_reference' => false,
'class' => 'AppBundle:FormElementOption',
'label' => $element->getLabel(),
'multiple' => true,
'expanded' => true,
'choices' => $element->getMultiOptions(),
'choice_label' => 'label',
'mapped' => true,
'disabled' => $disable_all || $element->isDisabled() ? true : false
));
OptionTransformer:
public function __construct($element, $options)
{
$this->options = $options;
$this->element = $element;
}
public function transform($options)
{
$data = array();
if($options !== null) {
foreach($options as $opt) {
// TODO: Replace this with em->getReference vs EAGER FETCH ON ENTITY
$data[] = $opt->getOption();
}
}
return $data;
}
public function reverseTransform($options)
{
$new = new ArrayCollection();
foreach($options as $option) {
$added = false;
if($this->options !== null) {
foreach ($this->options as $opt) {
// sub exists
if ($opt->getOption() != null && $opt->getOption()->getId() == $option->getId()) {
$new->add($opt);
$added = true;
}
}
}
if(!$added) {
$subOption = new FormSubmissionElementOption();
$subOption->setOption($option);
$subOption->setSubmissionElement($this->element);
$new->add($subOption);
}
}
if($this->element !== null && $this->element->getOptions() !== null) {
foreach($this->element->getOptions() as $opt) {
if(!$new->contains($opt)) {
$this->element->getOptions()->removeElement($opt);
}
}
}
return $new;
}