2017-09-23 7 views
1

2分割フォームを処理するコントローラアクションメソッドがあります。各フォームは、自分のエンティティWorkflowのいくつかのプロパティを処理します。最初のフォームを送信した後、問題なく2番目のフォームを作成してレンダリングできます。今問題:第二フォームを送信した後Symfony 3 - フォームモデルデータがフィールドで表されないプロパティ値を失う

は、最初の形式で設定したすべての値の情報(ここではどんな違いがありませんかhandleRequestsubmitを呼び出すときに、エンティティオブジェクトのみのデータを保持していることを意味しなくなっていますプロパティを最初の形式で設定し、いくつかの値を適切に解決することさえできません。ここ

は、(いくつかのコメント付き)コントローラである:

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' 
      )); 
     } 
    } 

$secondFormPartを提出するときに$firstFormPartに提出された$workflowのプロパティ値を保持するにはどうすればよいですか?

+0

:クラス、現在のセッションで渡されたワークフローを保存し、buildFormが取得するときにそれを取得するには、2時間のフォームを提出すると呼ばれますか?私の推測では、 'PraWorkflowTransitionsType'は' PraWorkflowStatesType'のフィールドを含んでいないので、2回目のサブミット後には最初のフォームのデータを持つことはできません。 –

+0

@JoryGeertsあなたは天才です!こんにちはMcfly、他に何か*各フォームは、私のエンティティ 'ワークフロー' *のいくつかのプロパティを処理しますか?もちろん、 'PraWorkflowTransitionsType'は' PraWorkflowStatesType'のフィールドを含んでいません!それは質問のタイトルの一部です*フォームモデルデータは、***フィールド***で表されていないプロパティ値を失います。 – goulashsoup

+0

@JoryGeerts 2番目のサブミッションの後に*を書くと、最初のフォームのデータを持つことはできません。もちろん、私は必要なすべての値を含むオブジェクトを渡すので、Symfonyは主な問題であるフォームをレンダリングした後にこれらの値を保存しません! – goulashsoup

答えて

1

フォームがsecondFormデータのみで再度送信されるため、firstFormデータが失われています。

あなたがそれらを維持するための3つの方法があります。

1)にnamestatesをバックに設定$secondFormPart = $this->createForm(WorkflowTransitionsType::class, $workflow);

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

問合せにfirstFormからデータを設定$workflowエンティティの場合、この例では、クエリ変数secondFormをチェックして、最初のフォームが送信されたかどうかを確認できます

2)いくつかの隠しフィールドで次のPATCHリクエストにfirstFormからデータを設定し

あなたは、いくつかのhidden form type

3とfirstFormからのデータを処理するためにsecondFormを変更する必要があります)第2フォームを返す前にセッション内のデータを設定する

まず、エンティティはインターフェイスSerializableを実装し、メソッドserializeを宣言する必要があります。その

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

それを格納するために使用されるserialize方法などD unserialize

。あなたがセッションに全体の実体を格納しているので

+0

1. symfonyがレスポンス/リクエストサイクル中にオブジェクトを保持する方法はありません。隠しフィールド自体を追加することによって?それはその問題の目的です。 2.今度は要求クエリソリューションについては、しなかった。 3. hiddinフィールドを追加することは、エンティティと最初のフォームが変更できるため、非常に動的ではありません。 4.何らかの理由で、セッションにオブジェクトを格納するときに、インタフェース 'Serializable'を使用する必要はありません。 – goulashsoup

+0

1.いいえ、2つの異なるリクエストを送信しているため、2番目のリクエストは最初のデータを認識していないので、最初のデータも提出する必要があります。 2.私はそれを追加します 3. 2番目のフォームでは、最初のフォームを拡張し、タイプを非表示に動的に変更することができます(テストはしませんでしたが、理論的にはうまくいくはずです)。 4.確かに、symfonyはあなたのために:) – oktapodia

+0

しかし、3.ポイントが汚れているように見えるかもしれません。また、firstFormデータの配列を取得しているエンティティでゲッターを宣言することもできます。次に、 のようなサブフォームを使用できます。 'buildForm'メソッドの中のsecondFormTypeには、 $ builder-> add( 'firstForm'、FirstFormType :: class);というサブフォームを使用できます。 'disable'または' hidden'オプション付き – oktapodia

0

あなたが使用することができ、このソリューションは多くのあなたのアプリケーションのパフォーマンスが低下しますunserialize

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

あなたはこの方法で、バックで設定することができますプロパティマップ。参照してくださいmapped propertyまたはallow_extra_fields

+0

こんにちは、McFly?あなたが提案したリンクは['default:true'](https://symfony.com/doc/current/reference/forms/types/form .html#mapped)これは、私の場合、すべてのフィールドがマップされていることを意味します。なぜなら、 'mapped =' false'をどこにも設定していないからです!もちろん、私が新しく追加した[_unsatisfying solution_](https://stackoverflow.com/a/46601434/5233188)よりも嫌いなプロパティ値を保持するために、いくつかのexraフィールドを追加するまで、 'allow_extra_fields'は何も変更しません。 。 – goulashsoup

+0

無礼にする必要はありません、それはちょうど長いショットでした。私はちょうどあなたに方向を教えようとしました、申し訳ありません。質問:なぜ2番目のフォームを入れ子にすることはできませんか? 2番目の値がnullでない場合、それは満足すべきものです。同じエンティティを使用する2つのフォームタイプを持つことができます。 https://symfony.com/doc/current/form/form_collections.html – Marco

0

どのように私は$ secondFormPartを提出する際に$ firstFormPartに提出$ワークフローのプロパティ値を保持することができますか?だからここ

は私(不満足)ソリューションです:

  1. 私は時に引数として渡すとWorkflowTransitionsTypeクラスである$secondFormPartのための私のフォームクラスにオプションとして現在のセッションを渡します私は012の内側私有財産としてセッションを保存

    $secondFormPart = $this->createForm(WorkflowTransitionsType::class, $workflow, array(
        'session' => $this->get('session') 
    )); 
    
  2. :コントローラのアクションメソッド内createFormを呼び出しますあなたは `PraWorkflowStatesType`と` PraWorkflowTransitionsType`の両方を投稿することができます

    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' 
         )); 
        } 
    } 
    
関連する問題