Symfony 2.7 - 多个嵌套/嵌入表单

时间:2016-08-03 10:09:22

标签: forms symfony nested-forms

如果有人能帮助我,我真的很感激。我花了几个小时解决这个问题并找不到解决方案。

代码工作正常(见下文),只要我不需要PersonType类中的第二个AddressType类。这样的事情不起作用:

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

相同Type的多个FormType将具有相同的HTML-ID,因此Symfony不会呈现第二个地址。关于这个话题我有几个问题:

  1. 即使我只需要两个地址,还是有另一种方式(我不需要用户能够动态添加另一个地址),我是否需要CollectionType?

  2. 我们假设我需要CollectionType。在开始时,CollectionType为空。但我从一开始就需要两个地址。我在Symfony-Documentation中发现了以下内容,从一开始就创建了一些集合项: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);
    
  3. 现在,请看看我的控制器。我甚至不创建一个实体。我只创建了主要的FormType,其中嵌入了所有其他Formtypes。我如何或在哪里创建这两个地址?甚至我的整个控制器代码都错了?

    1. 是否有解决方案告诉symfony第一个地址与第二个地址不同?如何设置每个AddressType的名称/ ID? (此solution不再有效。)

    2. 我的控制器中的表单生成真的没问题吗?我没有将实体或数组传递给表单。它有效,但我想知道为什么......否则我不知道如何将所需的所有实体传递给表格(合同,人员,地址,客户,付款等)。

      < / LI>

      提前致谢!

      控制器:

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

      表单类型:

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

      人类类别:

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

      地址类型类:

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

      客户类型类:

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

      数据库关联

      合同:人= 1:n(所有方:人,合同中人员的ArrayCollection)

      人:地址= 1:n(拥有方:地址,地址的ArrayCollection)

      人:客户= 1:1(所有方:客户,客户是个人)

      修改 Cerad对解决方案2的代码编辑 控制器 - 创建所有对象

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

      我替换了

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

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

      现在我收到以下异常: 表单的视图数据应该是标量,数组或\ ArrayAccess的实例,但是类Dmm \ Bundle \ GenericFormBundle \ Entity \ Contract的实例。您可以通过设置&#34; data_class&#34;来避免此错误。选项&#34; Dmm \ Bundle \ GenericFormBundle \ Entity \ Contract&#34;或者添加一个视图转换器,将类Dmm \ Bundle \ GenericFormBundle \ Entity \ Contract的实例转换为标量,数组或\ ArrayAccess的实例。

      在ContractType中,data_class设置为正确的类。仅在PrivateRentDepositType中,data_class设置为null。但PrivateRentDepositType是不同类型的集合。我试图将data_class设置为&#39; Dmm \ Bundle \ GenericFormBundle \ Entity \ Contract&#39;但这导致了另一个例外:无论是财产还是合同还是#34;也没有其中一种方法&#34; getContract()&#34;,&#34; contract()&#34;,&#34; isContract()&#34;,&#34; hasContract()&# 34;,&#34; __ get()&#34;在课堂上存在且具有公共访问权限&#34; Dmm \ Bundle \ GenericFormBundle \ Entity \ Contract&#34;。

1 个答案:

答案 0 :(得分:0)

您可以使用两种基本方法。

鉴于每个人都需要两个且只有两个地址,那么最快的方法就是:

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

将address1 / address2 getter / setter添加到Person实体。在内部,Person仍然可以在任何数组中存储地址。但就表单类型而言,每个表单都可以单独访问。其他一切都应该有用。

第二种方法更多的是Symfonyish,主要涉及创建和初始化新合同并将其传递给表单。这将消除在提交表单后进行如此多处理的需要。所以在你的控制器中你会有:

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

当然,创建合同(也就是聚合根)还有很多,然后显示的是什么,但你应该明白。我会把它全部移动到某个地方的createContract工厂方法中。这种方法允许您向个人实体添加两个地址,这反过来意味着您可以使用开箱即用的表单集合。

最后一点:表格在S3.x中发生了重大变化。新的FormType()不再有效。考虑更新至至少S2.8(具有新的表单内容),否则如果你移动到S3 +,你将面临重大改写。