2016-08-03 21 views
0

Ich würde mich sehr freuen, wenn mir jemand helfen könnte. Ich habe einige Stunden mit diesem Problem verbracht und konnte keine Lösung finden.Symfony 2.7 - mehrere verschachtelte/eingebettete Formulare

Der Code funktioniert einwandfrei (siehe unten), solange ich keine zweite AddressType-Klasse innerhalb der PersonType-Klasse benötige. So etwas wie das funktioniert nicht:

// PersonType: 
     $builder 
      ->add('firstname', 'text') 
      ->add('middlename', 'text') 
      ->add('lastname', 'text') 
      ->add('address', new AddressType()) 
      ->add('address', new AddressType()); 

Mehrere FormTypes des gleichen Typs die gleiche HTML-IDs haben so Symfony die zweite Adresse nicht machen. Ich habe einige Fragen zu diesem Thema:

  1. benötige ich die Collection selbst wenn ich nur zwei Adressen benötigen oder gibt es eine andere Art und Weise (ich dynamisch eine andere Adresse hinzufügen den Benutzer nicht brauchen, um der Lage sein)?

  2. Nehmen wir an, ich brauche den CollectionType. Zu Beginn ist ein CollectionType leer. Aber ich brauche von Anfang an zwei Adressen. Ich fand die folgenden in der Symfony-Dokumentation einige Kollektionsteile von Anfang an zu schaffen: Form Collections

    // Controller: 
    $task = new Task(); 
    // dummy code - this is here just so that the Task has some tags 
    // otherwise, this isn't an interesting example 
    $tag1 = new Tag(); 
    $tag1->setName('tag1'); 
    $task->getTags()->add($tag1); 
    $tag2 = new Tag(); 
    $tag2->setName('tag2'); 
    $task->getTags()->add($tag2); 
    

jetzt bitte an meinem Controller aussehen. Ich erschaffe nicht einmal eine Entität. Ich erstelle nur den Haupt-FormType, in den alle anderen Formtypen eingebettet sind. Wie oder wo kann ich die zwei Adressen erstellen? Oder ist sogar mein ganzer Controller-Code falsch?

  1. Gibt es eine Lösung, um symfony mitzuteilen, dass die erste Adresse nicht mit der zweiten Adresse übereinstimmt? Wie kann ich den Namen/die ID jedes AddressType festlegen? (Diese solution funktioniert nicht mehr.)

  2. Ist die Formulargenerierung in meinem Controller wirklich in Ordnung? Ich übergebe keine Entität oder ein Array an das Formular. Es funktioniert, aber ich frage mich warum ... Ansonsten weiß ich nicht, wie ich alle Entitäten, die ich brauche, an das Formular (Vertrag, Person, Adresse, Kunde, Bezahlung usw.) weiterleiten kann.

Vielen Dank im Voraus!

Controller:

class DefaultController extends Controller{ 
/** 
* @return array 
* @Route("/private") 
* @Template("DmmGenericFormBundle:Default:index.html.twig") 
*/ 
public function indexAction(Request $request) 
{ 
    // This formtype class has all other form type classes 
    $privateRentDepositForm = new PrivateRentDepositType(); 

    $form = $this->createForm($privateRentDepositForm); 

    $form->handleRequest($request); 

    if($form->isValid()){ 

     $contract =   $form->get('contract')->getData(); 
     $privateContractDetails = $form->get('privateContractDetails')->getData(); 

     $customer =   $form->get('customer')->getData(); 
     $customerPerson = $form->get('customer')->get('person')->getData(); 

     $privateRealEstate =   $form->get('privateRealEstate')->getData(); 
     $privateRealEstateAddress = $form->get('privateRealEstate')->get('address')->getData(); 

     $landlord =   $form->get('privateRealEstate')->get('landlord')->getData(); 
     $landlordPerson = $form->get('privateRealEstate')->get('landlord')->get('person')->getData(); 

     $caretakerPerson = $form->get('privateRealEstate')->get('landlord')->get('caretaker')->get('person')->getData(); 

     $payment =   $form->get('payment')->getData(); 

     $contract->addPerson($customerPerson); 
     $contract->addPerson($landlordPerson); 
     $contract->addPerson($caretakerPerson); 
     $contract->setPrivateContractDetails($privateContractDetails); 

     $customer->setPayment($payment); 

     $landlord->setPrivateRealEstate($privateRealEstate); 

     $privateRealEstate->addCustomer($customer); 
     $privateRealEstate->setLandlord($landlord); 


     $em = $this->get('doctrine')->getManager(); 
     $em->persist($contract); 
     $em->flush(); 
    } 
    return array('form' => $form->createView()); 
} 

Formulartyp Klassen:

class PrivateRentDepositType extends AbstractType 
{ 
    /** 
    * @param FormBuilderInterface $builder 
    * @param array $options 
    */ 
    public function buildForm(FormBuilderInterface $builder, array $options) 
    { 

     $builder 
      ->add('contract', new ContractType()) 
      ->add('privateContractDetails', new PrivateContractDetailsType()) 
      ->add('customer', new CustomerType()) 
      ->add('privateRealEstate', new PrivateRealEstateType()) 
      ->add('payment', new PaymentType()) 
      ->add('save', 'submit'); 
    } 

    /** 
    * @param OptionsResolverInterface $resolver 
    */ 
    public function configureOptions(OptionsResolver $resolver) 
    { 
     $resolver->setDefaults(array(
      'data_class' => null, 
      'csrf_protection' => false 
     )); 
    } 

    /** 
    * @return string 
    */ 
    public function getName() 
    { 
     return 'FormStep'; 
    } 
} 

Person Typ Klasse:

class PersonType extends AbstractType 
{ 
    /** 
    * @param FormBuilderInterface $builder 
    * @param array $options 
    */ 
    public function buildForm(FormBuilderInterface $builder, array $options) 
    { 
     $builder 
      ->add('firstname', 'text') 
      ->add('middlename', 'text') 
      ->add('lastname', 'text') 
      ->add('address', new AddressType()); 

    } 

    /** 
    * @param OptionsResolverInterface $resolver 
    */ 
    public function configureOptions(OptionsResolver $resolver) 
    { 
     $resolver->setDefaults(
      [ 
       'data_class' => 'Dmm\Bundle\GenericFormBundle\Entity\Person', 
       'config' => null, 
      ] 
     ); 
    } 

    /** 
    * @return string 
    */ 
    public function getName() 
    { 
     return 'dmm_bundle_genericformbundle_person'; 
    } 
} 

Adresse Typ Klasse:

class AddressType extends AbstractType 
{ 

    /** 
    * @param FormBuilderInterface $builder 
    * @param array $options 
    */ 
    public function buildForm(FormBuilderInterface $builder, array $options) 
    { 
     $builder 
      ->add('street', 'text') 
      ->add('housenumber', 'text') 
      ->add('zip', 'text') 
      ->add('city', 'text') 
      ->add('extra', 'text') 
      ->add('country', 'text') 
      ->add('postOfficeBox', 'integer') 
     ; 
    } 

    /** 
    * @param OptionsResolverInterface $resolver 
    */ 
    public function configureOptions(OptionsResolver $resolver) 
    { 
     $resolver->setDefaults(
      [ 
       'data_class' => 'Dmm\Bundle\GenericFormBundle\Entity\Address', 
       'config'  => null, 
      ] 
     ); 
    } 

    /** 
    * @return string 
    */ 
    public function getName() 
    { 
     return 'dmm_bundle_genericformbundle_address'; 
    } 
} 

Kundentyp Klasse:

class CustomerType extends AbstractType 
{ 
    /** 
    * @param FormBuilderInterface $builder 
    * @param array $options 
    */ 
    public function buildForm(FormBuilderInterface $builder, array $options) 
    { 
     $builder 
      ->add('person', new PersonType()) 
      ->add('birthplace', 'text') 
      ->add('email', 'email') 
      ->add('phone', 'text'); 

    } 

    /** 
    * @param OptionsResolverInterface $resolver 
    */ 
    public function configureOptions(OptionsResolver $resolver) 
    { 
     $resolver->setDefaults(
      [ 
       'data_class' => 'Dmm\Bundle\GenericFormBundle\Entity\Customer', 
       'config'  => null, 
      ] 
     ); 
    } 

    /** 
    * @return string 
    */ 
    public function getName() 
    { 
     return 'dmm_bundle_genericformbundle_customer'; 
    } 
} 

DB Verbände

Vertrag: Person = 1: n (Owning Seite: Person, Arraycollection für Person im Vertrag)

Person: Adresse = 1: n (Eigentümerseite: Adresse, ArrayCollection für persönliche Adresse)

Person: Kunde = 1: 1 (Owning Seite: Kunde, Kunde ist eine Person)

Edit: -Code bearbeiten für Lösung 2 durch CERAD -Controller - Erstellen Sie alle Objekte

$contract = new Contract(); 
    $contractDetails = new PrivateContractDetails(); 
    $contract->setPrivateContractDetails($contractDetails); 


    $customer1Person = new Person(); 
    $customer1Address1 = new Address(); 
    $customer1Address1->setAddressType('someAddress'); 
    $customer1Address2 = new Address(); 
    $customer1Address2->setAddressType('someAddress'); 
    $customer1Person->addAddress($customer1Address1); 
    $customer1Person->addAddress($customer1Address2); 
    $contract->addPerson($customer1Person); 
    $customer1 = new Customer(); 
    $customer1->setPerson($customer1Person); 

    $customer2Person = new Person(); 
    $customer2Address1 = new Address(); 
    $customer2Address1->setAddressType('someAddress'); 
    $customer2Address2 = new Address(); 
    $customer2Address2->setAddressType('someAddress'); 
    $customer2Person->addAddress($customer2Address1); 
    $customer2Person->addAddress($customer2Address2); 
    $contract->addPerson($customer2Person); 
    $customer2 = new Customer(); 
    $customer2->setPerson($customer2Person); 


    $landlordPerson = new Person(); 
    $landlordPersonAddress = new Address(); 
    $landlordPersonAddress->setAddressType('someAddress'); 
    $landlordPerson->addAddress($landlordPersonAddress); 
    $contract->addPerson($landlordPerson); 
    $landlord = new Landlord(); 
    $landlord->setPerson($landlordPerson); 


    $prEstate = new PrivateRealEstate(); 
    $prEstateAddress = new Address(); 
    $prEstateAddress->setAddressType('someAddress'); 
    $prEstate->setAddress($prEstateAddress); 
    $landlord->setPrivateRealEstate($prEstate); 
    $prEstate->addCustomer($customer1); 
    $prEstate->addCustomer($customer2); 


    $caretakerPerson = new Person(); 
    $caretakerAddress = new Address(); 
    $caretakerAddress->setAddressType('someAddress'); 
    $caretakerPerson->addAddress($caretakerAddress); 
    $contract->addPerson($caretakerPerson); 
    $caretaker = new Caretaker(); 
    $caretaker->addLandlord($landlord); 
    $caretaker->setPerson($caretakerPerson); 

    $payment = new Payment(); 
    $customer1->setPayment($payment); 

    $privateRentDepositForm = new PrivateRentDepositType(); 


    $form = $this->createForm($privateRentDepositForm, $contract); 

und ich ersetzt

->add('address', new AddressType()); 

mit

->add('address', 'collection', array(
    'type' => new AddressType() 
)) 

Jetzt bekomme ich die folgende Ausnahme: Die Ansicht Daten des Formulars wird erwartet, vom Typ Skalar, Array oder eine Instanz von \ Arrayaccess zu sein, aber ist eine Instanz der Klasse Dmm \ Bundle \ GenericFormBundle \ Entity \ Vertrag. Sie können diesen Fehler vermeiden, indem Sie die Option "data_class" auf "Dmm \ Bundle \ GenericFormBundle \ Entity \ Contract" setzen oder einen View-Transformer hinzufügen, der eine Instanz der Klasse Dmm \ Bundle \ GenericFormBundle \ Entity \ Contract in Skalar, Array oder transformiert eine Instanz von \ ArrayAccess.

In ContractType wird die Datenklasse auf die richtige Klasse festgelegt. Nur in PrivateRentDepositType wird die Datenklasse auf Null gesetzt. Aber PrivateRentDepositType ist eine Sammlung verschiedener Typen. Ich habe versucht, die data_class auf 'Dmm \ Bundle \ GenericFormBundle \ Entity \ Contract' zu setzen, aber das führt zu einem anderen excepiton: Weder die Eigenschaft "contract" noch eine der Methoden "getContract()", "contract()", "isContract() "," hasContract() "," __get() "existiert und hat öffentlichen Zugriff in der Klasse" Dmm \ Bundle \ GenericFormBundle \ Entity \ Contract ".

Antwort

0

Es gibt zwei grundlegende Ansätze, die Sie verwenden können.

Da jede Person zwei und nur zwei Adressen benötigt dann wäre der schnellste Ansatz zu tun:

// Form type 
    $builder 
     ->add('firstname', 'text') 
     ->add('middlename', 'text') 
     ->add('lastname', 'text') 
     ->add('address1', new AddressType(), 
     ->add('address2', new AddressType()); 

hinzufügen address1/Address2 Getter/Setter auf Ihre Person Einheit.Intern kann die Person weiterhin Adressen in einem beliebigen Array speichern. Aber soweit der Formulartyp geht, ist jeder einzeln zugänglich. Alles andere sollte funktionieren.

Der zweite Ansatz ist ein bisschen mehr Symfonyish und beinhaltet im Wesentlichen erstellen und initialisieren einen neuen Vertrag und übergibt es an das Formular. Das würde die Notwendigkeit für so viel Verarbeitung nach der Einreichung des Formulars beseitigen. Also in Ihrem Controller würden Sie haben:

$contract = new Contract(); 
$customerPerson = new CustomerPersion(); 
$contract->addPerson($customerPerson); 
... 
$privateContractDetails = new PrivateContractDetails() 
... 
$privateRentDeposit = [ 
    'contract' => $contract, 
    'privateContractDetails' => new $privateContractDetails, 
    ... rest of objects ... 
]; 
... 
$form = $this->createForm($privateRentDepositForm,$privateRentDeposit); 
--- 
if ($form->isValid()) { 
    $em->persist($contract); 

Es gibt natürlich noch viel mehr zu der Erstellung Ihres Vertrages (auch bekannt als Aggregat root) dann, was gezeigt wird, aber Sie sollten die Idee. Ich würde es irgendwo in eine createContract-Factory-Methode verschieben. Dieser Ansatz ermöglicht es Ihnen, zwei Adressen zu Ihren persönlichen Entitäten hinzuzufügen, was wiederum bedeutet, dass Sie die Formularsammlung aus der Box verwenden können.

Eine letzte Anmerkung: Formulare haben sich in S3.x geändert. new FormType() funktioniert nicht mehr. Überlege, ob du mindestens auf S2.8 updest (was den neuen Form-Kram hat), ansonsten wirst du einem größeren Neuschreiben gegenüberstehen, wenn du jemals zu S3 + wechselst.

+0

Vielen Dank! Die erste Lösung funktioniert gut. Aber ich bekomme eine Ausnahme mit der zweiten. Ich habe meinen Beitrag bearbeitet. Es wäre toll, wenn du mir wieder helfen könntest. Danke im Voraus. – Zebula

+0

Sieht aus wie $ privateRentDeposit ist nur ein Array von zusätzlichen Objekten. Meine Antwort wurde aktualisiert. Ich habe gerade einige Ihrer Fragen bearbeitet. Dies ist eine ziemlich komplizierte Form, die bearbeitet wird. Möglicherweise versuchen Sie, ein Objekt auf einmal hinzuzufügen, anstatt zu versuchen, das gesamte Formular zu debuggen. – Cerad

+0

Danke, Cerad! Sie haben mir sehr geholfen! Es scheint, dass mein Code einige Refactoring benötigt. Aber das Array von Objekten an den Formtyp übergeben hat funktioniert! – Zebula