Issue 1:
Hi,

Currently it's possible to create and populate fields based on an other one by using Listeners as in sample http://symfony.com/doc/current/cookbook/form/dynamic_form_modification.html#dynamic-generation-for-submitted-forms

But I seems to be not possible to make the field depend on multiple fields. For example, I would like to populate a list of available rooms depending on the type of room, start date and end date.

I tried by adding the following listener:

$formModifier = function (FormInterface $form, RoomType $roomType = null, \DateTime $startTime = null, \DateTime $endTime = null) {

$roomTypeId = ($roomType !== null) ? $roomType->getId() : null;

$hallConfigurations = $this->entityManager->getRepository('PTCNoventoBundle:HallConfiguration')
->getAllAvailableForCriteria($roomTypeId, $startTime, $endTime);

$form->add('hallConfiguration', 'entity', array(
'label' => 'hall',
'choices' => $hallConfigurations,
'class' => 'PTCNoventoBundle:HallConfiguration',
));
};

$builder->get('roomType')->addEventListener(
FormEvents::POST_SUBMIT,
function (FormEvent $event) use ($formModifier) {
$roomType = $event->getForm()->getData();
$startTime = $event->getForm()->getParent()->getData()->getStartTime(); // <- null
$endTime = $event->getForm()->getParent()->getData()->getEndTime(); // <- null
$formModifier($event->getForm()->getParent(), $roomType, $startTime, $endTime)
}
);

As you can see $startDate and $endDate are null at this time (not yet bound in the root form, I think). So I also tried to create a POST_SUBMIT listener attached to the root form but I have an error because I can't create new field on POST_SUBMIT at root form level.

If I replace the listener on roomType by a listener with SUBMIT EVENT at root level, I get always null in hallConfiguration (because the field was bound before).

$builder->addEventListener(
FormEvents::SUBMIT,
function (FormEvent $event) use ($formModifier) {

$roomType = $event->getForm()->get('roomType')->getData(); // <- ok - RoomType object
$startTime = $event->getForm()->get('startTime')->getData(); // <- ok - DateTime object
$endTime = $event->getForm()->get('endTime')->getData(); // <- ok - DateTime object

$formModifier($event->getForm(), $roomType, $startTime, $endTime);

$this->logger->info(print_r($event->getData()->getHallConfiguration(), true)); // <- null
}
);

It would be interesting to be able to support dynamic form with multiple dependent fields as in my sample.

@webmozart Any ideas on this possible feature ?

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.