Issue 1:
Hi all,

If there's something that bothers us is to create form to fill out polymorphic objects. Normally, when you have a polymorphic object, you first need to know the type and then build the form. Currently, there is no an elegant way to do this trick. You may create multiple embedded forms, or use two actions and do it in two steps. You also may use events to dinamically "merge" the form, but all events and options of the other form will be lost (for instance, if the CarType is set to cascade validation, and the TruckType is not, how can you solve it using events?), besides others drawbacks. Later, when you want to edit the object, you have another "problem": check the type of object and then instantiate the correct form. Of course, you can create a service, but would be very nice if this job was made by the factory automatically.

After a long time thinking about, I think that the following pseudocode may be a good approach:

class Vehicle
{
public $model;
}
class Car extends Vehicle {}
class Truck extends Vehicle {}

abstract class VehicleType extends AbstractType {}
class CartType extends VehicleType {}
// 'data_class' => 'Acme\Bundle\VehicleBundle\Entity\Car'
class TruckType extends VehicleType {}
// 'data_class' => 'Acme\Bundle\VehicleBundle\Entity\Truck'
class MotocycleType extends VehicleType {}
// 'data_class' => 'Acme\Bundle\VehicleBundle\Entity\Motocycle'

class VehicleBuilderType extends VehicleType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('type', 'choice', array(
'choices' => $options['inheritance_map'],
));

// The model field ($vehicle->model) will be displayed on the first stage of the form (type selection) and will fill the field of the inherited form on bind
parent::buildForm($builder, $options);
}

public function getDefaultOptions(array $options)
{
return array(
'inheritance' => true,
'inheritance_discriminator' => 'type',
'inheritance_map' => array(
'car' => 'cart_type',
'truck' => 'truck_type',
'motocycle' => 'motocycle_type',
));
}
}

public function actionCreate()
{
$form = $factory->create('vehicle_type');

if ($request->isMethod('POST')) {
$form->bind($request); // array('type' => 'car', 'wheels' => 4);
echo get_class($form->getData()); // prints Acme\Bundle\VehicleBundle\Entity\Car
}
}

public function actionEdit($id)
{
$vehicle = new Car(); // load from database...
$form = $factory->create('vehicle_type', $vehicle);
echo get_class($form); // prints Acme\Bundle\VehicleBundle\Form\CarType
}

For now, a quickly solution may be a data_class based class guessing in the FormFactory :

$factory->createForData(new Car()); // returns a CarType

What you think?

Issue 2:
We long have the problem of creating fields that depend on the value of other fields now. See also:

#3767

#3768

#4548

I want to propose a solution that seems feasible from my current point of view.

Currently, I can think of two different APIs:

API 1

<?php

$builder->addIf(function (FormInterface $form) {
return $form->get('field1')->getData() >= 1
&& !$form->get('field2')->getData();
}, 'myfield', 'text');

$builder->addUnless(function (FormInterface $form) {
return $form->get('field1')->getData() < 1
|| $form->get('field2')->getData();
}, 'myfield', 'text');

API 2

<?php

$builder
->_if(function (FormInterface $form) {
return $form->get('field1')->getData() >= 1
&& !$form->get('field2')->getData();
})
->add('myfield', 'text')
->add('myotherfield', 'text')
->_endif()
;

$builder
->_switch(function (FormInterface $form) {
return $form->get('field1')->getData();
})
->_case('foo')
->_case('bar')
->add('myfield', 'text', array('foo' => 'bar'))
->add('myotherfield', 'text')
->_case('baz')
->add('myfield', 'text', array('foo' => 'baz'))
->_default()
->add('myfield', 'text')
->_endswitch()
;

The second API obviously is a lot more expressive, but also a bit more complicated than the first one.

Please give me your opinions on what API you prefer or whether you can think of further limitations in these APIs.

Implementation

The issue of creating dependencies between fields can be solved by a lazy dependency resolution graph like in the OptionsResolver.

During form prepopulation, the conditions are invoked with a FormPrepopulator object implementing FormInterface. When FormPrepopulator::get('field') is called, "field" is prepopulated. If "field" is also dependent on some condition, that condition will be evaluated now in order to construct "field". After evaluating the condition, fields are added or removed accordingly.

During form binding, the conditions are invoked with a FormBinder object, that also implements FormInterface. This object works like FormPrepopulator, only that it binds the fields instead of filling them with default data.

In both cases, circular dependencies can be detected and reported.