How to use the Virtual Form Field Option
Edit this pageWarning: You are browsing the documentation for Symfony 2.1, which is no longer maintained.
Consider upgrading your projects to Symfony 6.3.
How to use the Virtual Form Field Option
The virtual
form field option can be very useful when you have some
duplicated fields in different entities.
For example, imagine you have two entities, a Company
and a Customer
:
1 2 3 4 5 6 7 8 9 10 11 12 13
// src/Acme/HelloBundle/Entity/Company.php
namespace Acme\HelloBundle\Entity;
class Company
{
private $name;
private $website;
private $address;
private $zipcode;
private $city;
private $country;
}
1 2 3 4 5 6 7 8 9 10 11 12 13
// src/Acme/HelloBundle/Entity/Customer.php
namespace Acme\HelloBundle\Entity;
class Customer
{
private $firstName;
private $lastName;
private $address;
private $zipcode;
private $city;
private $country;
}
Like you can see, each entity shares a few of the same fields: address
,
zipcode
, city
, country
.
Now, you want to build two forms: one for a Company
and the second for
a Customer
.
Start by creating a very simple CompanyType
and CustomerType
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
// src/Acme/HelloBundle/Form/Type/CompanyType.php
namespace Acme\HelloBundle\Form\Type;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilder;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
class CompanyType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('name', 'text')
->add('website', 'text');
}
}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
// src/Acme/HelloBundle/Form/Type/CustomerType.php
namespace Acme\HelloBundle\Form\Type;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\AbstractType;
class CustomerType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('firstName', 'text')
->add('lastName', 'text');
}
}
Now, to deal with the four duplicated fields. Here is a (simple) location form type:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
// src/Acme/HelloBundle/Form/Type/LocationType.php
namespace Acme\HelloBundle\Form\Type;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
class LocationType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('address', 'textarea')
->add('zipcode', 'text')
->add('city', 'text')
->add('country', 'text');
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'virtual' => true
));
}
public function getName()
{
return 'location';
}
}
You don't actually have a location field in each of your entities, so you
can't directly link LocationType
to CompanyType
or CustomerType
.
But you absolutely want to have a dedicated form type to deal with location (remember, DRY!).
The virtual
form field option is the solution.
You can set the option 'virtual' => true
in the setDefaultOptions()
method
of LocationType
and directly start using it in the two original form types.
Look at the result:
1 2 3 4 5 6 7
// CompanyType
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('foo', new LocationType(), array(
'data_class' => 'Acme\HelloBundle\Entity\Company'
));
}
1 2 3 4 5 6 7
// CustomerType
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('bar', new LocationType(), array(
'data_class' => 'Acme\HelloBundle\Entity\Customer'
));
}
With the virtual option set to false (default behavior), the Form Component
expects each underlying object to have a foo
(or bar
) property that
is either some object or array which contains the four location fields.
Of course, you don't have this object/array in your entities and you don't want it!
With the virtual option set to true, the Form component skips the foo
(or bar
)
property, and instead "gets" and "sets" the 4 location fields directly
on the underlying object!
Note
Instead of setting the virtual
option inside LocationType
, you
can (just like with any options) also pass it in as an array option to
the third argument of $builder->add()
.