我在做什么

我正在从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;
    }