-
-
Notifications
You must be signed in to change notification settings - Fork 9.7k
Description
We long have the problem of creating fields that depend on the value of other fields now. See also:
- [Form] Make dynamic modification of children possible *after* other children have been bound #3767
- [Form] added ChildDataEvent #3768
- [Form] Make bind() method bind while fields are being added #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.