Symfony 3 - 表单模型数据丢失了属性值

时间:2017-09-23 16:14:30

标签: php forms symfony symfony-forms

我有一个控制器动作方法,它应该处理两个分割形式。每个表单只处理我的实体Workflow的一些属性。提交第一个表单后,我可以创建并呈现第二个表单而不会出现问题。现在问题是:

提交第二个表单后,第一个表单中设置的所有值的信息都消失了,这意味着当调用submit(或handleRequest这里没有任何区别时)实体对象只能保存以第一种形式设置的属性数据,甚至无法正确解析某些值。

这是Controller(有一些评论):

public function createWorkflowAction(Request $request, Project $project, Workflow $workflow = null) {   

    if(!$workflow) {
        $workflow = new Workflow($project);
    }

    $firstFormPart = $this->createForm(WorkflowStatesType::class, $workflow);

    // $firstFormPart->handleRequest($request);
    $firstFormPart->submit($request->get($firstFormPart->getName()), false);

    $secondFormPart = $this->createForm(WorkflowTransitionsType::class, $workflow);
    // secondFormPart is created correct with all values after submitting $firstFormPart and calling submit

    if($firstFormPart->isSubmitted() && $firstFormPart->isValid()) {
        return $this->render('@MyBundle/Workflow/workflow_edit_create_second_part.html.twig', array(
            'form'   => $secondFormPart->createView(),
        ));
        // This will render correctly with all values submitted in the $firstFormPart
    }

    $secondFormPart->submit($request->get($secondFormPart->getName()), false);
    // $secondFormPart->handleRequest($request);
    // HERE IS THE PROBLEM -> After submitting the $secondFormPart all property values set in the $firstFormPart are gone

    if($secondFormPart->isSubmitted() && $secondFormPart->isValid()) {
        dump($workflow);
        die();
    }

    return $this->render('@MyBundle/Workflow/workflow_edit_create_first_part.html.twig', array(
        'form' => $firstFormPart->createView(),
    ));
}

WorkflowStatesType

class WorkflowStatesType extends AbstractType {

        /**
         * @var \Doctrine\ORM\Mapping\ClassMetadata
         */
        private $classMetadata;

        /**
         * WorkflowType constructor.
         * @param EntityManager $em
         */

        public function __construct(EntityManager $em) {
            $this->classMetadata = $em->getClassMetadata(Workflow::class);
        }

        public function buildForm(FormBuilderInterface $builder, array $options) {
            $builder
                ->setMethod('PATCH')
                ->add('name', TextType::class, array(
                    'label' => 'nameTrans',
                    'attr'  => array('maxLength' => $this->classMetadata->getFieldMapping('name')['length']),
                ))
                ->add('states',  CollectionType::class, array(
                    'entry_type'        => StateType::class,
                    'allow_add'         => true,
                    'error_bubbling'    => false,
                    'by_reference'      => false,
                    'label'             => 'workflowStatesTrans',
                ))
                ->add('next', SubmitType::class, array(
                    'label' => 'nextFormPartTrans',
                ));
        }

        public function configureOptions(OptionsResolver $resolver) {
            $resolver->setDefaults(array(
                'data_class'            => Workflow::class,
                'translation_domain'    => 'My_Bundle',
            ));
        }

    }

WorkflowTransitionsType

class WorkflowTransitionsType extends AbstractType {

        /**
         * @var Workflow
         */
        private $workflow;

        /**
         * @var Session
         */
        private $session;

        /**
         * {@inheritdoc}
         */
        public function buildForm(FormBuilderInterface $builder, array $options) {

            /** @var Workflow $workflow */
            $this->workflow = $options['data'];

                $builder
                    ->setMethod('PATCH')
                    ->add('initialState', ChoiceType::class, array(
                        'choices'           => $this->workflow->getStates(),
                        'choice_label'      => function($state) {
                            return ($state && $state instanceof State) ? $state->getStatekey() : 'noVal';
                        },
                        'choice_value'      => function($state) {
                            return ($state && $state instanceof State) ? $state->getStatekey() : 'noVal';
                        },

                        // This combination of 'expanded' and 'multiple' implements a select box
                        'expanded'          => false,
                        'multiple'          => false,
                    ))
                    ->add('transitions', CollectionType::class, array(
                        'entry_type'        => TransitionType::class,
                        'allow_add'         => true,
                        'allow_delete'      => true,
                        'error_bubbling'    => false,
                        'by_reference'      => false,
                        'label'             => 'transitionsTrans',
                        'entry_options'     => array(
                            'states'    => $this->workflow->getStates(),
                        ),
                    ))
                    ->add('save', SubmitType::class, array(
                        'label'             => 'submitTrans',
                    ));
        }

        public function configureOptions(OptionsResolver $resolver) {
            $resolver->setDefaults(array(
                'data_class'            => Workflow::class,
                'translation_domain'    => 'My_Bundle',
            ));
            $resolver->setRequired(array(
                'session'
            ));
        }
    }

如何在提交$workflow时保留$firstFormPart中提交的$secondFormPart的媒体资源价值?

3 个答案:

答案 0 :(得分:1)

由于仅使用secondForm数据再次提交表单,因此您将丢失firstForm数据。

你有三种方法可以保留它们:

1)将firstForm中的数据设置为查询

// Insert that instead of the `return $this->render` for the second form
$url = $this->generateUrl(
    $request->attributes->get('_route'),
    array_merge(
        $request->query->all(),
        array('secondForm' => true, 'name' => $workflow->getName(), 'states' => $workflow->getStates()) // change the param
    )
);
return $this->redirect($url);

$secondFormPart = $this->createForm(WorkflowTransitionsType::class, $workflow);之前

namestates设置回$workflow实体,在此示例中,您可以检查查询变量secondForm以了解是否已提交第一个表单

2)将firstForm中的数据设置为具有隐藏字段的下一个PATCH请求

你必须修改secondForm来处理来自firstForm的数据hidden form type

3)在返回第二个表单

之前,在会话中设置数据

首先,您的实体必须实施界面Serializable并声明方法serializeunserialize

像那样

$this->get('session')->set('workflow', $workflow);

方法serialize将用于存储它。

您可以使用方法unserialize

进行设置
$session = $this->get('session');
$workflow = new Workflow();
$workflow->unserialize($session->get('workflow'));

因为您将整个实体存储到会话中,所以此解决方案会降低应用程序的性能

答案 1 :(得分:0)

您可以使用映射的属性。请参阅mapped propertyallow_extra_fields

答案 2 :(得分:0)

  

如何在提交$ secondFormPart时保存$ firstFormPart中提交的$ workflow的属性值?

所以这是我的(不满意的)解决方案:

  1. 我将当前会话作为选项传递给CV_EXTERNAL $secondFormPart类的表单类,并在Controller中调用WorkflowTransitionsType时将其作为参数传递行动方法:

    createForm
  2. 我将会话保存为$secondFormPart = $this->createForm(WorkflowTransitionsType::class, $workflow, array( 'session' => $this->get('session') )); 类中的私有属性,保存当前会话中传递的工作流,并在WorkflowTransitionsType被调用时检索它2.提交表单时的时间:

    buildForm