2016-02-03 8 views
5

私は大きなエンティティと大きなフォームを持っています。私のエンティティを更新するとき、私はフォームの一部をajax呼び出しだけでレンダリングします。クライアント側では、Jqueryとhtml5 FormDataを使用していますので、私のフォーム内でファイルを送信することもできます。レンダリングされないフィールドがプロセスでnullに設定されないようにするには、PATCHメソッドを使用しています。Symfony3:PATCHメソッドを使用してブール値をfalseに更新するには

は、フィールドがリクエストに存在しないあるのでとき、それはsymfonyによってあるとしてが左です。

フィールドIが更新されると、trueに設定されたブール値(チェックボックスにレンダリングされた)がfalseに設定されているため、要求に渡されないため、の更新は無視されます

未確認のチェックボックスをリクエストに表示させる簡単な方法はありますか?

EDIT
私はthis questionに、フェリックスクリングさんのコメントのおかげで、要求に表示されるように未チェックのチェックボックスを強制する方法を見つけました:

$("input:checkbox:not(:checked)").each(function() { 
    formData.set($(this).attr('name'), formData.has($(this).attr('id')) ? '1' : '0'); 
}); 

残念ながら、が、これは私の問題を解決しませんでした、 symfonyの動作のため:
- PUTを使用している場合、ブール値フィールドがリクエストに表示される場合、その値に関係なくに設定されます(「0」または「false」の場合でも)。
- PATCHメソッドを使用すると、要求に表示されないフィールドは無視されます。

DataTransformerで解決できますか? (私はそれを使用したことがありません)

答えて

1

あるので、あなたはまた、二番目のパラメータを省略することができ、作業のソリューションを行く そのは改善される可能性があります。リクエストに表示されるように未チェックのチェックボックスを強制的に

this questionのフェリックスクリングさんのコメントのおかげで、私はこれは私のAJAX要求の前にJS追加しました:その後

$("input:checkbox:not(:checked)").each(function() { 
    formData.set($(this).attr('name'), formData.has($(this).attr('id')) ? '1' : '0'); 
}); 

、symfonyの側では、私がしなければなりませんでしたどの文字列でもtrue、ヌル値ではfalseを返すBooleanToStringTransformerビヘイビアをオーバーライドします。最後の行を変更した場合、値がの値(デフォルトでは "1")と一致しない場合はfalseを返します。したがって、フォームから返された値が "0"の場合、期待どおりが偽になります。

the docs
public function reverseTransform($value) 
{ 
    if (null === $value) { 
     return false; 
    } 

    if (!is_string($value)) { 
     throw new TransformationFailedException('Expected a string.'); 
    } 

    return ($this->trueValue === $value); // initially: "return true;" 
} 

、私はAjaxCheckboxType

自分DataTransformerを作っただけでなく、カスタム残念ながら、symfonyは両方 DataTransformers(鉱山と元1)、後の1つを使用するようですそれ以外は動作しませんでした。ドキュメント内では、TextTypeではなくCheckboxTypeとなっていますが、これは私が遭遇した問題を説明するものでなければなりません。

CheckboxType classを自分のAjaxCheckboxTypeにコピー&ペーストして、DataTransformerの呼び出しを変更して使用しました。

DataTransformerを完全にオーバーライドするのははるかに良い解決策ですが、どうすればよいか分かりません。

+1

$ builder-> resetViewTransformers()でデータトランスフォーマをリセットできます。次に、独自のDataTransformerを追加します。 – Falc

+0

@Falc Excellent!それはまさに私が必要なものです! – Roubi

+0

良い解決策!私はこの動作を 'accept_null_on_patch'フォームフィールドオプションで簡単に設定できるように' CheckboxType'を拡張した 'PatchableCheckboxTypeExtension'の中でこれを実装しました。 trueに設定すると、すべてのViewTransformerが削除され、代わりに 'PatchableCheckboxBooleanToStringTransformer'が追加されます。 – spackmat

4

あなたは絶対的に正しいです、symfonyはこの方法が原因this line in Request HandlerのPATCHであれば、それを無視します:

$form->submit($data, 'PATCH' !== $method); 

さて、私は一般的にその場合は、PUT要求を使用することを示唆していますオプションですが、そうでなければFormInterface::submit($submittedData, $clearMissing = true)の2番目の引数はあなたの後です。

「適切な」方法は、おそらくSymfony\Component\Form\RequestHandlerInterfaceの独自の実装を作成することで、$clearMissingtrueに強制します。

その他の方法は簡単ですが、すべてのユースケースでうまくいかない場合があります。$form->submit()を直接使用してください。

次のコードをお持ちの場合:

$form->handleRequest($request); 

あなたが行うことができます。

$form->submit($request->get($form->getName()), true); 

trueがデフォルト値ここで

+0

あなたの答えをありがとう。すべてのフィールドでPATCHの動作を元に戻すことはできません。私はフォームのほんの一部をレンダリングしているので、レンダリングされたチェックボックスを除いて、更新時に不在フィールドをそのまま残す必要があります。私はチェックボックスが*チェックされていないときでさえ、それらがsubmit要求に現れるようにレンダリングする必要があると思います。 – Roubi

1

symfonyはちょうど正しくPATCH-ペイロードを準備し、箱から出して、これを処理します:)

symfonyのCheckboxTypeは、少なくとも現在のバージョン3.3(2.3以降、以下の更新を参照のように思える)で、入力を受け付けますnullの値で、「チェックされていない」と解釈されます(スニペットの3〜5行目のRoubi's really helpful answerにあります)。

クライアントサイドのAJAX-PATCH-controllerでは、application/merge-patch+jsonペイロードのチェックされていないチェックボックスフィールドの値をnullに設定していますが、すべて問題ありません。 CheckboxTypeの動作を上書きするフォーム拡張はまったく必要ありません。

問題:HTTP-POSTペイロードの値をnullに設定できないため、リクエスト本体内のJSON(またはその他の互換性のある)ペイロードでのみ動作します。Content-Type: application/merge-patch+jsonまたはこの場合にも、有効なJSONペイロード、下記の意志とPATCH-要求の場合

/** 
* @Route("/test/patch.json", name="test_patch") 
* @Method({"PATCH"}) 
*/ 
public function patchAction(\Symfony\Component\HttpFoundation\Request $request) 
{ 
    $form = $this->createFormBuilder(['checkbox' => true, 'dummyfield' => 'presetvalue'], ['csrf_protection' => false]) 
     ->setAction($this->generateUrl($request->get('_route'))) 
     ->setMethod('PATCH') 
     ->add('checkbox', \Symfony\Component\Form\Extension\Core\Type\CheckboxType::class) 
     ->add('dummyfield', \Symfony\Component\Form\Extension\Core\Type\TextType::class) 
     ->getForm() 
    ; 
    $form->submit(json_decode($request->getContent(), true), false); 

    return new \Symfony\Component\HttpFoundation\JsonResponse($form->getData()); 
} 

これを実証するために簡単なデモ

、あなたはこの単純化されたテストコントローラを使用することができます起こる:

null値でチェックボックスを提出

{"checkbox": null} 

falseにあるチェックボックスを上書きします:

{"checkbox": false, "dummyfield": "presetvalue"} 

と(前もそうでした)trueにするチェックボックスを設定します

{"checkbox": "1"} 

元の値であるチェックボックスを提出

{"checkbox": true, "dummyfield": "presetvalue"} 

と提出されたチェックボックスの値なし

{"dummyfield": "requestvalue"} 

は、その初期の真の状態のチェックボックスを残すのみdummyfieldを上書きします:

{"checkbox": true, "dummyfield": "requestvalue"} 

これは、PATCH要求は余分な隠し入力が必要で、動作してはならない方法です。クライアントサイドでJSONペイロードを適切に準備するだけで、うまくいきます。

OKですが、拡張ChoiceType/EntityTypeはどうですか?

チェックボックスやラジオボタンをレンダリングし、送信されたペイロード内のチェックされたチェックボックス/ラジオボタンの値の単純なリストを期待する、拡張されたChoiceType(EntityTypeのような子タイプ)の場合、この単純な解決法は機能しません。フォーム拡張を実装し、PRE_SUBMITのイベントリスナをこれらのフィールドに追加し、送信されなかったチェックボックス/ラジオボタンをnullに設定しました。このイベントリスナーはCheckboxTypeのクロージャーリスナーの後に呼び出さなければならず、単純リスト["1", "3"]をチェックボックスの値をキーと値として持つハッシュに転送します。私には-1の優先度があります。だから、閉鎖から出てくる["1" => "1", "3" => "3"]は私のリスナーの後に["1" => "1", "2" => null, "3" => "3"]になる。

$builder->addEventListener(
    \Symfony\Component\Form\FormEvents::PRE_SUBMIT, 
    function (\Symfony\Component\Form\FormEvent $event) { 
     if ('PATCH' === $event->getForm()->getRoot()->getConfig()->getMethod() 
      && $event->getForm()->getConfig()->getOption('expanded', false) 
     ) { 
      $data = $event->getData(); 
      foreach ($event->getForm()->all() as $type) { 
       if (!array_key_exists($type->getName(), $data)) { 
        $data[$type->getName()] = null; 
       } 
      } 
      ksort($data); 
      $event->setData($data); 
     } 
    }, -1 
); 

更新:/Symfony/Component/Form/Form.phpで提出し、メソッド内でこのコメントを見ている(それはsymfonyの2.3以降があります):私のPatchableChoiceTypeExtensionのリスナーは、基本的にはこのようになります

// Treat false as NULL to support binding false to checkboxes. 
// Don't convert NULL to a string here in order to determine later 
// whether an empty value has been submitted or whether no value has 
// been submitted at all. This is important for processing checkboxes 
// and radio buttons with empty values. 

アップデート2017-09-12:ラジオグループは、チェックボックスグループと同じ方法で処理する必要があります。そのため、リスナーは両方を処理します。選択して複数選択すると、すぐに使用できるようになります。

関連する問題