2016-12-12 2 views
0

Ich habe eine /checkout JSON API-Endpunkt, der eine optionale billingAddress Parameter neben anderen Parametern wie E-Mail und deliveryAddress ermöglicht.Symfony Form - Erlaube Entfernung von verschachtelten Formular verbundenen Einheit

Diese Adressen sind in einer Address Einheit gespeichert, die sich auf eine Order Einheit bezieht.

Alles funktioniert gut, wenn ein Benutzer ihre Rechnungsadresse eingibt, aber wenn ein Benutzer eine zuvor übermittelte Rechnungsadresse entfernt, kann ich keine Möglichkeit finden, die billingAddress Einheit zu entfernen. Im Idealfall würde ich die folgende JSON POST-Anfrage verwenden, um die Rechnungsadresse zu entfernen.

{ 
    "email": "[email protected]", 
    "deliveryAddress": { 
     "line1": "1 Box Lane" 
    }, 
    "billingAddress": null 
} 

Ist dies bei Symfony-Formularen überhaupt möglich?

Siehe unten für eine vereinfachte Erklärung des aktuellen Setups.

Entities

/** 
* @ORM\Entity 
*/ 
class Order 
{ 
    // ... 

    /** 
    * @var Address 
    * 
    * @ORM\OneToOne(targetEntity = "Address", cascade = {"persist", "remove"}) 
    * @ORM\JoinColumn(name = "deliveryAddressId", referencedColumnName = "addressId") 
    */ 
    private $deliveryAddress; 

    /** 
    * @var Address 
    * 
    * @ORM\OneToOne(targetEntity = "Address", cascade = {"persist", "remove"}, orphanRemoval = true) 
    * @ORM\JoinColumn(name = "billingAddressId", referencedColumnName = "addressId", nullable = true) 
    */ 
    private $billingAddress; 

    public function setDeliveryAddress(Address $deliveryAddress = null) 
    { 
     $this->deliveryAddress = $deliveryAddress; 
     return $this; 
    } 

    public function getDeliveryAddress() 
    { 
     return $this->deliveryAddress; 
    } 

    public function setBillingAddress(Address $billingAddress = null) 
    { 
     $this->billingAddress = $billingAddress; 
     return $this; 
    } 

    public function getBillingAddress() 
    { 
     return $this->billingAddress; 
    } 

    // ... 
} 

.

/** 
* @ORM\Entity 
*/ 
class Address 
{ 
    // ... 

    /** 
    * @var string 
    * 
    * @ORM\Column(type = "string", length = 45, nullable = true) 
    */ 
    private $line1; 

    // ... 
} 

Forms

class CheckoutType extends AbstractType 
{ 
    public function buildForm(FormBuilderInterface $builder, array $options) 
    { 
     $builder 
      ->add('email', EmailType::class) 
      ->add('deliveryAddress', AddressType::class, [ 
       'required' => true 
      ]) 
      ->add('billingAddress', AddressType::class, [ 
       'required' => false 
      ]); 
    } 

    public function configureOptions(OptionsResolver $resolver) 
    { 
     $resolver->setDefaults([ 
      'data_class' => Order::class, 
      'csrf_protection' => false, 
      'allow_extra_fields' => true, 
      'cascade_validation' => true 
     ]); 
    } 

    public function getBlockPrefix() 
    { 
     return ''; 
    } 
} 

.

class AddressType extends AbstractType 
{ 
    public function buildForm(FormBuilderInterface $builder, array $options) 
    { 
     $builder 
      // ... 
      ->add('line1', TextType::class); 
      // ... 
    } 

    public function configureOptions(OptionsResolver $resolver) 
    { 
     $resolver->setDefaults([ 
      'data_class' => Address::class, 
      'allow_extra_fields' => true 
     ]); 
    } 

    public function getBlockPrefix() 
    { 
     return ''; 
    } 
} 

Antwort

0

Formularereignisse sind, was Sie brauchen: https://symfony.com/doc/current/form/events.html

Zum Beispiel, wenn Sie das billingAddress Feld nach dem Formular-Vorlage entfernen möchten Sie das tun können:

public function buildForm(FormBuilderInterface $builder, array $options) 
{ 
    $builder 
     ->add('email', EmailType::class) 
     ->add('deliveryAddress', AddressType::class, [ 
      'required' => true 
     ]) 
     ->add('billingAddress', AddressType::class, [ 
      'required' => false 
     ]); 

    $builder->addEventListener(FormEvents::PRE_SUBMIT, function(FormEvent $event) { 
     $form = $event->getForm(); 
     $data = $event->getData(); 

     if (empty($data['billingAddress'])) { 
      $form->remove('billingAddress'); 
     } 
    }); 
} 

sorgfältig lesen Sie die Dokumentation zu wissen Sie, welches Ereignis das beste für Ihr Szenario sein wird.

+0

Interessante Idee, haben dies angepasst, wie einfach das Element entfernen die Entität nicht aktualisiert. Wird später ein Update veröffentlichen, wenn ich eine Chance bekomme. Thnaks – Nick

0

Vielen Dank an Renan und Raffaels Antworten, wie sie führte mich die unten Lösung zu entdecken, die sowohl für einen Teil PATCH und vollständige POST-Anfrage funktioniert.

class CheckoutType extends AbstractType 
{ 
    /** @var bool */ 
    private $removeBilling = false; 

    public function buildForm(FormBuilderInterface $builder, array $options) 
    { 
     $builder 
      ->add('email', EmailType::class) 
      ->add('deliveryAddress', AddressType::class, [ 
       'constraints' => [new Valid] 
      ]) 
      ->add('billingAddress', AddressType::class, [ 
       'required' => false, 
       'constraints' => [new Valid] 
      ]) 
      ->addEventListener(FormEvents::PRE_SUBMIT, [$this, 'onPreSubmit']) 
      ->addEventListener(FormEvents::POST_SUBMIT, [$this, 'onPostSubmit']); 
    } 

    public function onPreSubmit(FormEvent $event) 
    { 
     $data = $event->getData(); 
     $this->removeBilling = array_key_exists('billingAddress', $data) && is_null($data['billingAddress']); 
    } 

    public function onPostSubmit(FormEvent $event) 
    { 
     if ($this->removeBilling) { 
      $event->getData()->setBillingAddress(null); 
     } 
    } 

}