Symfony 2.7 - 多个 nested/embedded 表单
Symfony 2.7 - multiple nested/embedded Forms
如果有人能帮助我,我将不胜感激。我花了几个小时解决这个问题,但找不到解决方案。
代码工作正常(见下文),只要我不需要 PersonType Class 中的第二个 AddressType Class。这样的东西不起作用:
// PersonType:
$builder
->add('firstname', 'text')
->add('middlename', 'text')
->add('lastname', 'text')
->add('address', new AddressType())
->add('address', new AddressType());
相同类型的多个 FormType 将具有相同的 HTML-ID,因此 Symfony 不会呈现第二个地址。我有几个关于该主题的问题:
我是否需要 CollectionType,即使我只需要两个地址还是有其他方法(我不需要用户能够动态添加另一个地址)?
假设我需要 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);
现在,请看我的控制器。我什至不创建实体。我只创建嵌入了所有其他 Formtypes 的主 FormType。如何或在哪里可以创建这两个地址?甚至我的整个控制器代码都是错误的?
有没有办法告诉symfony第一个地址和第二个地址不一样?如何设置每个 AddressType 的 name/id? (此 solution 不再起作用。)
我的控制器真的可以生成表单吗?我不会将实体或数组传递给表单。它有效,但我不知道为什么......否则我不知道如何将我需要的所有实体传递给表格(合同,人员,地址,客户,付款等)。
提前致谢!
控制器:
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());
}
表单类型 Classes:
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:
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:
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:
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';
}
}
数据库关联
Contract:Person = 1:n(拥有方:Person,ArrayCollection for Person in Contract)
Person:Address = 1:n (Owning Side: Address, ArrayCollection for Address in Person)
Person:Customer = 1:1(拥有方:Customer,Customer IS A Person)
编辑:
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 的实例,但它是 class Dmm\Bundle\GenericFormBundle\Entity\Contract 的实例。您可以通过将 "data_class" 选项设置为 "Dmm\Bundle\GenericFormBundle\Entity\Contract" 或添加将 class Dmm\Bundle\GenericFormBundle\Entity\Contract 的实例转换为标量、数组或 \ 的实例的视图转换器来避免此错误ArrayAccess.
在 ContractType 中,data_class 设置在右侧 class。仅在 PrivateRentDepositType 中 data_class 设置为空。但是 PrivateRentDepositType 是不同类型的集合。我试图将 data_class 设置为 'Dmm\Bundle\GenericFormBundle\Entity\Contract' 但这会导致另一个异常:属性 "contract" 和方法之一 "getContract()"、"contract()", "isContract()", "hasContract()", "__get()" 在 class "Dmm\Bundle\GenericFormBundle\Entity\Contract".
中存在并具有 public 访问权限
您可以使用两种基本方法。
鉴于每个人只需要两个地址,那么最快的方法是:
// Form type
$builder
->add('firstname', 'text')
->add('middlename', 'text')
->add('lastname', 'text')
->add('address1', new AddressType(),
->add('address2', new AddressType());
将 address1/address2 getter/setters 添加到您的 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+,你将面临重大重写。
如果有人能帮助我,我将不胜感激。我花了几个小时解决这个问题,但找不到解决方案。
代码工作正常(见下文),只要我不需要 PersonType Class 中的第二个 AddressType Class。这样的东西不起作用:
// PersonType:
$builder
->add('firstname', 'text')
->add('middlename', 'text')
->add('lastname', 'text')
->add('address', new AddressType())
->add('address', new AddressType());
相同类型的多个 FormType 将具有相同的 HTML-ID,因此 Symfony 不会呈现第二个地址。我有几个关于该主题的问题:
我是否需要 CollectionType,即使我只需要两个地址还是有其他方法(我不需要用户能够动态添加另一个地址)?
假设我需要 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);
现在,请看我的控制器。我什至不创建实体。我只创建嵌入了所有其他 Formtypes 的主 FormType。如何或在哪里可以创建这两个地址?甚至我的整个控制器代码都是错误的?
有没有办法告诉symfony第一个地址和第二个地址不一样?如何设置每个 AddressType 的 name/id? (此 solution 不再起作用。)
我的控制器真的可以生成表单吗?我不会将实体或数组传递给表单。它有效,但我不知道为什么......否则我不知道如何将我需要的所有实体传递给表格(合同,人员,地址,客户,付款等)。
提前致谢!
控制器:
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());
}
表单类型 Classes:
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:
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:
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:
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';
}
}
数据库关联
Contract:Person = 1:n(拥有方:Person,ArrayCollection for Person in Contract)
Person:Address = 1:n (Owning Side: Address, ArrayCollection for Address in Person)
Person:Customer = 1:1(拥有方:Customer,Customer IS A Person)
编辑: 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 的实例,但它是 class Dmm\Bundle\GenericFormBundle\Entity\Contract 的实例。您可以通过将 "data_class" 选项设置为 "Dmm\Bundle\GenericFormBundle\Entity\Contract" 或添加将 class Dmm\Bundle\GenericFormBundle\Entity\Contract 的实例转换为标量、数组或 \ 的实例的视图转换器来避免此错误ArrayAccess.
在 ContractType 中,data_class 设置在右侧 class。仅在 PrivateRentDepositType 中 data_class 设置为空。但是 PrivateRentDepositType 是不同类型的集合。我试图将 data_class 设置为 'Dmm\Bundle\GenericFormBundle\Entity\Contract' 但这会导致另一个异常:属性 "contract" 和方法之一 "getContract()"、"contract()", "isContract()", "hasContract()", "__get()" 在 class "Dmm\Bundle\GenericFormBundle\Entity\Contract".
中存在并具有 public 访问权限您可以使用两种基本方法。
鉴于每个人只需要两个地址,那么最快的方法是:
// Form type
$builder
->add('firstname', 'text')
->add('middlename', 'text')
->add('lastname', 'text')
->add('address1', new AddressType(),
->add('address2', new AddressType());
将 address1/address2 getter/setters 添加到您的 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+,你将面临重大重写。