Outdated article!
If it's a technical article, take care, maybe some informations are not exact anymore.Otherwise, please keep in mind that this article was written quite a long time ago.
Wait! What? You already write this before! Oo
Yes I do! But in french! And it seems it was not very clear... So let me explain virtuals forms again and this time... in english!
We have 2 entities. A Company and a Customer :
<?php
namespace ...;
class Company
{
private $name;
private $website;
private $address;
private $zipcode;
private $city;
private $country;
// Some nice getters / setters here.
}
<?php
namespace ...;
class Customer
{
private $firstName;
private $lastName;
private $address;
private $zipcode;
private $city;
private $country;
// Some nice getters / setters here.
}
Like you can see, both of our entities have these fields: address
, zipcode
, city
, country
.
Now, we want to build 2 forms. One for create/update a Company and the second to create/update a Customer.
Of course, we have only two entities which have to contains some location informations... for now! Maybe later, some entities will have this fields. So, we have to find a solution to not duplicate our code!
First, we create very simple CompanyType and CustomerType:
<?php
namespace ...;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilder;
class CompanyType extends AbstractType
{
public function buildForm(FormBuilder $builder, array $options)
{
$builder
->add('name', 'text')
->add('website', 'text')
;
}
public function getDefaultOptions(array $options)
{
return array(
'data_class' => '...\Company',
);
}
public function getName()
{
return 'company';
}
}
<?php
namespace ...;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilder;
class CustomerType extends AbstractType
{
public function buildForm(FormBuilder $builder, array $options)
{
$builder
->add('firstName', 'text')
->add('lastName', 'text')
;
}
public function getDefaultOptions(array $options)
{
return array(
'data_class' => '...\Customer',
);
}
public function getName()
{
return 'customer';
}
}
Definitely nothing complicated here.
Now, we have to deal with our four duplicated fields... Here is a (simple) location FormType:
<?php
namespace ...;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilder;
class LocationType extends AbstractType
{
public function buildForm(FormBuilder $builder, array $options)
{
$builder
->add('address', 'textarea')
->add('zipcode', 'string')
->add('city', 'string')
->add('country', 'text')
;
}
public function getDefaultOptions(array $options)
{
return array(
);
}
public function getName()
{
return 'location';
}
}
We can't specify a data_class option in this FormType because, we don't have a Location Entity.
We don't have a location field in our entity so we can't directly link our LocationType.
Of course, we absolutely want to have a dedicated FormType to deal with location (remember, DRY!)
There is a solution!
We can set the option 'virtual' => true
in the getDefaultOptions
method of our LocationType and directly start use it in our 2 first types.
Look at the result:
<?php
// CompanyType
public function buildForm(FormBuilder $builder, array $options)
{
$builder->add('foo', new LocationType());
}
<?php
// CustomerType
public function buildForm(FormBuilder $builder, array $options)
{
$builder->add('bar', new LocationType());
}
With the virtual option set to false (default behavior), the Form Component expect a Foo (or Bar) object or array which contains our four location fields. Of course, we don't have this object/array in our entities and we don't want it!
With the virtual option set to true, the Form Component skip our Foo (or Bar) object or array. So, it directly access to our 4 location fields which are in the parent entity!
(One more time, thank to Alexandre Salomé for the tips)