I would really appreciate if someone could help me. I spend some hours with this problem and could not find a solution.
The code works fine (see below), as long as I don't need a second AddressType Class within PersonType Class. Something like this does not work:
// PersonType:
$builder
->add('firstname', 'text')
->add('middlename', 'text')
->add('lastname', 'text')
->add('address', new AddressType())
->add('address', new AddressType());
Multiple FormTypes of the same Type will have the same HTML-IDs so Symfony does not render the second address. I have several questions on that topic:
Do I need the CollectionType even if I need only two address or is there another way (I don't need the user to be able to add dynamically another address)?
Let's assume I need the CollectionType. At the beginning a CollectionType is empty. But I need two addresses from the start. I found the following in the Symfony-Documentation to create some collection items from the start: 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);
Now, please look at my controller. I don't even create a entity. I only create the main FormType which has all other Formtypes embedded. How or where can I create the two addresses? Or even my whole Controller-Code is wrong?
Is there a solution to tell symfony that the first address is not the same as the second address? How can I set the name/id of each AddressType? (This solution does not work anymore.)
Is the form generation really ok in my controller? I don't pass a entity or an array to the form. It works, but I wonder why.... Otherwise I don't know how to pass all the entities I need to the form (contract, person, address, customer, payment, etc.).
Thanks in advance!
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());
}
Form Type 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';
}
}
Person Type 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';
}
}
Address Type 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';
}
}
Customer Type 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';
}
}
DB Associations
Contract:Person = 1:n (Owning Side: Person, ArrayCollection for Person in Contract)
Person:Address = 1:n (Owning Side: Address, ArrayCollection for Address in Person)
Person:Customer = 1:1 (Owning Side: Customer, Customer IS A Person)
Edit: Code edit for Solution 2 by Cerad Controller - create all objects
$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);
And I replaced
->add('address', new AddressType());
with
->add('address', 'collection', array(
'type' => new AddressType()
))
Now I get the following exception: The form's view data is expected to be of type scalar, array or an instance of \ArrayAccess, but is an instance of class Dmm\Bundle\GenericFormBundle\Entity\Contract. You can avoid this error by setting the "data_class" option to "Dmm\Bundle\GenericFormBundle\Entity\Contract" or by adding a view transformer that transforms an instance of class Dmm\Bundle\GenericFormBundle\Entity\Contract to scalar, array or an instance of \ArrayAccess.
In ContractType the data_class is set to the right class. Only in PrivateRentDepositType the data_class is set to null. But PrivateRentDepositType is a collection of diffrent types. I tried to set the data_class to 'Dmm\Bundle\GenericFormBundle\Entity\Contract' but that leads to another excepiton: Neither the property "contract" nor one of the methods "getContract()", "contract()", "isContract()", "hasContract()", "__get()" exist and have public access in class "Dmm\Bundle\GenericFormBundle\Entity\Contract".