Symfony 3 - Form model data loses property values which are not represented by fields

前端 未结 3 573
有刺的猬
有刺的猬 2021-01-25 05:07

I have a controller action method which should handle a two-splitted form. Each form handles just a few properties of my Entity Workflow. After submitting the first

相关标签:
3条回答
  • 2021-01-25 05:16

    Because the form is submitted again with only the secondForm data, you are losing the firstForm data.

    You have 3 ways to keep them:

    1) Set the data from the firstForm into the query

    // 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);
    

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

    Set back the name and states into the $workflow entity, in this example you can check the query variable secondForm to know if the first form was submitted or not

    2) Set the data from the firstForm into the next PATCH request with some hidden field

    You have to modify the secondForm to handle the data from the firstForm with some hidden form type

    3) Set the data in the session before returning the second form

    First of all, your entity will have to implement the interface Serializable and declare the method serialize and unserialize

    like that

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

    The method serialize will be used to store it.

    You can set in back with the method unserialize

    $session = $this->get('session');
    $workflow = new Workflow();
    $workflow->unserialize($session->get('workflow'));
    

    Because you are storing the whole entity into the session, this solution will decrease a lot the performance of your application

    0 讨论(0)
  • 2021-01-25 05:24

    How can I hold the property values of the $workflow submitted in the $firstFormPart when submitting the $secondFormPart?

    So here is my (unsatisfying) solution:

    1. I pass the current session as an option to my form class for the $secondFormPart which is the WorkflowTransitionsType class with passing it as an argument when calling createForm inside the Controller action method:

      $secondFormPart = $this->createForm(WorkflowTransitionsType::class, $workflow, array(
          'session'   => $this->get('session')
      ));
      
    2. I save the session as a private property inside the WorkflowTransitionsType class, save the passed workflow in the current session and retrieve it when buildForm gets called a 2. time when submitting the form:

      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'];
      
              /** @var Session $session */
              $this->session = $options['session'];
      
              // If the workflow is stored in the session we know that this method is called a 2. time!
              if($this->session->has($this->getBlockPrefix() . '_workflow')) $this->workflow = $this->session->get($this->getBlockPrefix() . '_workflow');
      
                  $builder
                      ->setMethod('PATCH')
                      ->addEventListener(FormEvents::PRE_SET_DATA, function(FormEvent $event) {
                          dump($event);
                          // This always gets called AFTER storing the workflow if it is present in the current session
                          $this->session->set($this->getBlockPrefix() . '_workflow', $this->workflow);
                      })
                      ->addEventListener(FormEvents::PRE_SUBMIT, function(FormEvent $event) {
                          // Here we manipulating the passed workflow data by setting all previous values! 
                          $eventForm = $event->getForm();
      
                          /** @var Workflow $submitWorkflow */
                          $submitWorkflow = $eventForm->getData();
      
                          $submitWorkflow->setName($this->workflow->getName());
                          foreach($this->workflow->getStates() as $state) $submitWorkflow->addState($state);
      
                          $eventForm->setData($submitWorkflow);
                      })
                      ->addEventListener(FormEvents::POST_SUBMIT, function(FormEvent $event) {
                          // After submitting the workflow object is no longer required!
                          $this->session->remove($this->getBlockPrefix() . '_workflow');
                      })
                      ->add('initialState', ChoiceType::class, array(
                          ...
                          // Didn´t change (look at my question)
                      ))
                      ->add('transitions', CollectionType::class, array(
                          ...
                          // Didn´t change (look at my question)
                      ))
                      ->add('save', SubmitType::class, array(
                          ...
                          // Didn´t change (look at my question)
                      ));
          }
      
          /**
           * {@inheritdoc}
           */
          public function configureOptions(OptionsResolver $resolver) {
              $resolver->setDefaults(array(
                  'data_class'            => Workflow::class,
                  'translation_domain'    => 'MyBundle',
              ));
              $resolver->setRequired(array(
                  // This is necessary to prevent an error about an unknown option!
                  'session'
              ));
          }
      }
      
    0 讨论(0)
  • 2021-01-25 05:39

    You can use the property mapped. See mapped property or allow_extra_fields

    0 讨论(0)
提交回复
热议问题