<p dir="auto">Hi,</p>
<p dir="auto">Currently it's possible to create and populate fields based on an other one by using Listeners as in sample <a href="http://symfony.com/doc/current/cookbook/form/dynamic_form_modification.html#dynamic-generation-for-submitted-forms" rel="nofollow">http://symfony.com/doc/current/cookbook/form/dynamic_form_modification.html#dynamic-generation-for-submitted-forms</a></p>
<p dir="auto">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.</p>
<p dir="auto">I tried by adding the following listener:</p>
<div class="highlight highlight-text-html-php notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="$formModifier = function (FormInterface $form, RoomType $roomType = null, \DateTime $startTime = null, \DateTime $endTime = null) {

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

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

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

$builder-&gt;get('roomType')-&gt;addEventListener(
    FormEvents::POST_SUBMIT,
    function (FormEvent $event) use ($formModifier) {
        $roomType = $event-&gt;getForm()-&gt;getData();
        $startTime = $event-&gt;getForm()-&gt;getParent()-&gt;getData()-&gt;getStartTime(); // &lt;- null
        $endTime = $event-&gt;getForm()-&gt;getParent()-&gt;getData()-&gt;getEndTime(); // &lt;- null
        $formModifier($event-&gt;getForm()-&gt;getParent(), $roomType, $startTime, $endTime)           
    }
);"><pre class="notranslate"><span class="pl-s1"><span class="pl-c1">$</span>formModifier</span> = <span class="pl-k">function</span> (<span class="pl-smi"><span class="pl-smi">FormInterface</span></span> <span class="pl-s1"><span class="pl-c1">$</span>form</span>, <span class="pl-smi"><span class="pl-smi">RoomType</span></span> <span class="pl-s1"><span class="pl-c1">$</span>roomType</span> = <span class="pl-c1">null</span>, <span class="pl-smi"><span class="pl-smi">\<span class="pl-v">DateTime</span></span></span> <span class="pl-s1"><span class="pl-c1">$</span>startTime</span> = <span class="pl-c1">null</span>, <span class="pl-smi"><span class="pl-smi">\<span class="pl-v">DateTime</span></span></span> <span class="pl-s1"><span class="pl-c1">$</span>endTime</span> = <span class="pl-c1">null</span>) {

    <span class="pl-s1"><span class="pl-c1">$</span>roomTypeId</span> = (<span class="pl-s1"><span class="pl-c1">$</span>roomType</span> !== <span class="pl-c1">null</span>) ? <span class="pl-s1"><span class="pl-c1">$</span>roomType</span>-&gt;<span class="pl-en">getId</span>() : <span class="pl-c1">null</span>;

    <span class="pl-s1"><span class="pl-c1">$</span>hallConfigurations</span> = <span class="pl-s1"><span class="pl-c1">$</span><span class="pl-smi">this</span></span>-&gt;<span class="pl-c1">entityManager</span>-&gt;<span class="pl-en">getRepository</span>(<span class="pl-s">'PTCNoventoBundle:HallConfiguration'</span>)
        -&gt;<span class="pl-en">getAllAvailableForCriteria</span>(<span class="pl-s1"><span class="pl-c1">$</span>roomTypeId</span>, <span class="pl-s1"><span class="pl-c1">$</span>startTime</span>, <span class="pl-s1"><span class="pl-c1">$</span>endTime</span>);

    <span class="pl-s1"><span class="pl-c1">$</span>form</span>-&gt;<span class="pl-en">add</span>(<span class="pl-s">'hallConfiguration'</span>, <span class="pl-s">'entity'</span>, <span class="pl-en">array</span>(
        <span class="pl-s">'label'</span> =&gt; <span class="pl-s">'hall'</span>,
        <span class="pl-s">'choices'</span> =&gt; <span class="pl-s1"><span class="pl-c1">$</span>hallConfigurations</span>,
        <span class="pl-s">'class'</span> =&gt; <span class="pl-s">'PTCNoventoBundle:HallConfiguration'</span>,
    ));
};

<span class="pl-s1"><span class="pl-c1">$</span>builder</span>-&gt;<span class="pl-en">get</span>(<span class="pl-s">'roomType'</span>)-&gt;<span class="pl-en">addEventListener</span>(
    <span class="pl-v">FormEvents</span>::<span class="pl-c1">POST_SUBMIT</span>,
    <span class="pl-k">function</span> (<span class="pl-smi"><span class="pl-smi">FormEvent</span></span> <span class="pl-s1"><span class="pl-c1">$</span>event</span>) <span class="pl-k">use</span> (<span class="pl-s1"><span class="pl-c1">$</span>formModifier</span>) {
        <span class="pl-s1"><span class="pl-c1">$</span>roomType</span> = <span class="pl-s1"><span class="pl-c1">$</span>event</span>-&gt;<span class="pl-en">getForm</span>()-&gt;<span class="pl-en">getData</span>();
        <span class="pl-s1"><span class="pl-c1">$</span>startTime</span> = <span class="pl-s1"><span class="pl-c1">$</span>event</span>-&gt;<span class="pl-en">getForm</span>()-&gt;<span class="pl-en">getParent</span>()-&gt;<span class="pl-en">getData</span>()-&gt;<span class="pl-en">getStartTime</span>(); <span class="pl-c">// &lt;- null</span>
        <span class="pl-s1"><span class="pl-c1">$</span>endTime</span> = <span class="pl-s1"><span class="pl-c1">$</span>event</span>-&gt;<span class="pl-en">getForm</span>()-&gt;<span class="pl-en">getParent</span>()-&gt;<span class="pl-en">getData</span>()-&gt;<span class="pl-en">getEndTime</span>(); <span class="pl-c">// &lt;- null</span>
        <span class="pl-s1"><span class="pl-c1">$</span>formModifier</span>(<span class="pl-s1"><span class="pl-c1">$</span>event</span>-&gt;<span class="pl-en">getForm</span>()-&gt;<span class="pl-en">getParent</span>(), <span class="pl-s1"><span class="pl-c1">$</span>roomType</span>, <span class="pl-s1"><span class="pl-c1">$</span>startTime</span>, <span class="pl-s1"><span class="pl-c1">$</span>endTime</span>)           
    }
);</pre></div>
<p dir="auto">As you can see <code class="notranslate">$startDate</code> and <code class="notranslate">$endDate</code> are null at this time (not yet bound in the root form, I think). So I also tried to create a <code class="notranslate">POST_SUBMIT</code> listener attached to the root form but I have an error because I can't create new field on <code class="notranslate">POST_SUBMIT</code> at root form level.</p>
<p dir="auto">If I replace the listener on <code class="notranslate">roomType</code> by a listener with <code class="notranslate">SUBMIT</code> EVENT at root level, I get always null in <code class="notranslate">hallConfiguration</code> (because the field was bound before).</p>
<div class="highlight highlight-text-html-php notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="$builder-&gt;addEventListener(
    FormEvents::SUBMIT,
    function (FormEvent $event) use ($formModifier) {

        $roomType = $event-&gt;getForm()-&gt;get('roomType')-&gt;getData(); // &lt;- ok - RoomType object
        $startTime = $event-&gt;getForm()-&gt;get('startTime')-&gt;getData(); // &lt;- ok - DateTime object
        $endTime = $event-&gt;getForm()-&gt;get('endTime')-&gt;getData(); // &lt;- ok - DateTime object

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

        $this-&gt;logger-&gt;info(print_r($event-&gt;getData()-&gt;getHallConfiguration(), true)); // &lt;- null
    }
);"><pre class="notranslate"><span class="pl-s1"><span class="pl-c1">$</span>builder</span>-&gt;<span class="pl-en">addEventListener</span>(
    <span class="pl-v">FormEvents</span>::<span class="pl-c1">SUBMIT</span>,
    <span class="pl-k">function</span> (<span class="pl-smi"><span class="pl-smi">FormEvent</span></span> <span class="pl-s1"><span class="pl-c1">$</span>event</span>) <span class="pl-k">use</span> (<span class="pl-s1"><span class="pl-c1">$</span>formModifier</span>) {

        <span class="pl-s1"><span class="pl-c1">$</span>roomType</span> = <span class="pl-s1"><span class="pl-c1">$</span>event</span>-&gt;<span class="pl-en">getForm</span>()-&gt;<span class="pl-en">get</span>(<span class="pl-s">'roomType'</span>)-&gt;<span class="pl-en">getData</span>(); <span class="pl-c">// &lt;- ok - RoomType object</span>
        <span class="pl-s1"><span class="pl-c1">$</span>startTime</span> = <span class="pl-s1"><span class="pl-c1">$</span>event</span>-&gt;<span class="pl-en">getForm</span>()-&gt;<span class="pl-en">get</span>(<span class="pl-s">'startTime'</span>)-&gt;<span class="pl-en">getData</span>(); <span class="pl-c">// &lt;- ok - DateTime object</span>
        <span class="pl-s1"><span class="pl-c1">$</span>endTime</span> = <span class="pl-s1"><span class="pl-c1">$</span>event</span>-&gt;<span class="pl-en">getForm</span>()-&gt;<span class="pl-en">get</span>(<span class="pl-s">'endTime'</span>)-&gt;<span class="pl-en">getData</span>(); <span class="pl-c">// &lt;- ok - DateTime object</span>

        <span class="pl-s1"><span class="pl-c1">$</span>formModifier</span>(<span class="pl-s1"><span class="pl-c1">$</span>event</span>-&gt;<span class="pl-en">getForm</span>(), <span class="pl-s1"><span class="pl-c1">$</span>roomType</span>, <span class="pl-s1"><span class="pl-c1">$</span>startTime</span>, <span class="pl-s1"><span class="pl-c1">$</span>endTime</span>);

        <span class="pl-s1"><span class="pl-c1">$</span><span class="pl-smi">this</span></span>-&gt;<span class="pl-c1">logger</span>-&gt;<span class="pl-en">info</span>(print_r(<span class="pl-s1"><span class="pl-c1">$</span>event</span>-&gt;<span class="pl-en">getData</span>()-&gt;<span class="pl-en">getHallConfiguration</span>(), <span class="pl-c1">true</span>)); <span class="pl-c">// &lt;- null</span>
    }
);</pre></div>
<p dir="auto">It would be interesting to be able to support dynamic form with multiple dependent fields as in my sample.</p>
<p dir="auto"><a class="user-mention notranslate" data-hovercard-type="user" data-hovercard-url="/users/webmozart/hovercard" data-octo-click="hovercard-link-click" data-octo-dimensions="link_type:self" href="https://github.com/webmozart">@webmozart</a> Any ideas on this possible feature ?</p>
<p dir="auto">We long have the problem of creating fields that depend on the value of other fields now. See also:</p>
<ul dir="auto">
<li><a class="issue-link js-issue-link" data-error-text="Failed to load title" data-id="3948579" data-permission-text="Title is private" data-url="https://github.com/symfony/symfony/issues/3767" data-hovercard-type="issue" data-hovercard-url="/symfony/symfony/issues/3767/hovercard" href="https://github.com/symfony/symfony/issues/3767">#3767</a></li>
<li><a class="issue-link js-issue-link" data-error-text="Failed to load title" data-id="3954416" data-permission-text="Title is private" data-url="https://github.com/symfony/symfony/issues/3768" data-hovercard-type="pull_request" data-hovercard-url="/symfony/symfony/pull/3768/hovercard" href="https://github.com/symfony/symfony/pull/3768">#3768</a></li>
<li><a class="issue-link js-issue-link" data-error-text="Failed to load title" data-id="4993683" data-permission-text="Title is private" data-url="https://github.com/symfony/symfony/issues/4548" data-hovercard-type="pull_request" data-hovercard-url="/symfony/symfony/pull/4548/hovercard" href="https://github.com/symfony/symfony/pull/4548">#4548</a></li>
</ul>
<p dir="auto">I want to propose a solution that seems feasible from my current point of view.</p>
<p dir="auto">Currently, I can think of two different APIs:</p>
<h5 dir="auto">API 1</h5>
<div class="highlight highlight-text-html-php notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="&lt;?php

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

$builder-&gt;addUnless(function (FormInterface $form) {
    return $form-&gt;get('field1')-&gt;getData() &lt; 1 
        || $form-&gt;get('field2')-&gt;getData();
}, 'myfield', 'text');"><pre class="notranslate"><span class="pl-ent">&lt;?php</span>

<span class="pl-s1"><span class="pl-c1">$</span>builder</span>-&gt;<span class="pl-en">addIf</span>(<span class="pl-k">function</span> (<span class="pl-smi"><span class="pl-smi">FormInterface</span></span> <span class="pl-s1"><span class="pl-c1">$</span>form</span>) {
    <span class="pl-k">return</span> <span class="pl-s1"><span class="pl-c1">$</span>form</span>-&gt;<span class="pl-en">get</span>(<span class="pl-s">'field1'</span>)-&gt;<span class="pl-en">getData</span>() &gt;= <span class="pl-c1">1</span> 
        &amp;&amp; !<span class="pl-s1"><span class="pl-c1">$</span>form</span>-&gt;<span class="pl-en">get</span>(<span class="pl-s">'field2'</span>)-&gt;<span class="pl-en">getData</span>();
}, <span class="pl-s">'myfield'</span>, <span class="pl-s">'text'</span>);

<span class="pl-s1"><span class="pl-c1">$</span>builder</span>-&gt;<span class="pl-en">addUnless</span>(<span class="pl-k">function</span> (<span class="pl-smi"><span class="pl-smi">FormInterface</span></span> <span class="pl-s1"><span class="pl-c1">$</span>form</span>) {
    <span class="pl-k">return</span> <span class="pl-s1"><span class="pl-c1">$</span>form</span>-&gt;<span class="pl-en">get</span>(<span class="pl-s">'field1'</span>)-&gt;<span class="pl-en">getData</span>() &lt; <span class="pl-c1">1</span> 
        || <span class="pl-s1"><span class="pl-c1">$</span>form</span>-&gt;<span class="pl-en">get</span>(<span class="pl-s">'field2'</span>)-&gt;<span class="pl-en">getData</span>();
}, <span class="pl-s">'myfield'</span>, <span class="pl-s">'text'</span>);</pre></div>
<h5 dir="auto">API 2</h5>
<div class="highlight highlight-text-html-php notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="&lt;?php

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

$builder
    -&gt;_switch(function (FormInterface $form) {
        return $form-&gt;get('field1')-&gt;getData();
    })
    -&gt;_case('foo')
    -&gt;_case('bar')
        -&gt;add('myfield', 'text', array('foo' =&gt; 'bar'))
        -&gt;add('myotherfield', 'text')
    -&gt;_case('baz')
        -&gt;add('myfield', 'text', array('foo' =&gt; 'baz'))
    -&gt;_default()
        -&gt;add('myfield', 'text')
    -&gt;_endswitch()
;"><pre class="notranslate"><span class="pl-ent">&lt;?php</span>

<span class="pl-s1"><span class="pl-c1">$</span>builder</span>
    -&gt;<span class="pl-en">_if</span>(<span class="pl-k">function</span> (<span class="pl-smi"><span class="pl-smi">FormInterface</span></span> <span class="pl-s1"><span class="pl-c1">$</span>form</span>) {
        <span class="pl-k">return</span> <span class="pl-s1"><span class="pl-c1">$</span>form</span>-&gt;<span class="pl-en">get</span>(<span class="pl-s">'field1'</span>)-&gt;<span class="pl-en">getData</span>() &gt;= <span class="pl-c1">1</span> 
            &amp;&amp; !<span class="pl-s1"><span class="pl-c1">$</span>form</span>-&gt;<span class="pl-en">get</span>(<span class="pl-s">'field2'</span>)-&gt;<span class="pl-en">getData</span>();
    })
        -&gt;<span class="pl-en">add</span>(<span class="pl-s">'myfield'</span>, <span class="pl-s">'text'</span>)
        -&gt;<span class="pl-en">add</span>(<span class="pl-s">'myotherfield'</span>, <span class="pl-s">'text'</span>)
    -&gt;<span class="pl-en">_endif</span>()
;

<span class="pl-s1"><span class="pl-c1">$</span>builder</span>
    -&gt;<span class="pl-en">_switch</span>(<span class="pl-k">function</span> (<span class="pl-smi"><span class="pl-smi">FormInterface</span></span> <span class="pl-s1"><span class="pl-c1">$</span>form</span>) {
        <span class="pl-k">return</span> <span class="pl-s1"><span class="pl-c1">$</span>form</span>-&gt;<span class="pl-en">get</span>(<span class="pl-s">'field1'</span>)-&gt;<span class="pl-en">getData</span>();
    })
    -&gt;<span class="pl-en">_case</span>(<span class="pl-s">'foo'</span>)
    -&gt;<span class="pl-en">_case</span>(<span class="pl-s">'bar'</span>)
        -&gt;<span class="pl-en">add</span>(<span class="pl-s">'myfield'</span>, <span class="pl-s">'text'</span>, <span class="pl-en">array</span>(<span class="pl-s">'foo'</span> =&gt; <span class="pl-s">'bar'</span>))
        -&gt;<span class="pl-en">add</span>(<span class="pl-s">'myotherfield'</span>, <span class="pl-s">'text'</span>)
    -&gt;<span class="pl-en">_case</span>(<span class="pl-s">'baz'</span>)
        -&gt;<span class="pl-en">add</span>(<span class="pl-s">'myfield'</span>, <span class="pl-s">'text'</span>, <span class="pl-en">array</span>(<span class="pl-s">'foo'</span> =&gt; <span class="pl-s">'baz'</span>))
    -&gt;<span class="pl-en">_default</span>()
        -&gt;<span class="pl-en">add</span>(<span class="pl-s">'myfield'</span>, <span class="pl-s">'text'</span>)
    -&gt;<span class="pl-en">_endswitch</span>()
;</pre></div>
<p dir="auto">The second API obviously is a lot more expressive, but also a bit more complicated than the first one.</p>
<p dir="auto">Please give me your opinions on what API you prefer or whether you can think of further limitations in these APIs.</p>
<h5 dir="auto">Implementation</h5>
<p dir="auto">The issue of creating dependencies between fields can be solved by a lazy dependency resolution graph like in the OptionsResolver.</p>
<p dir="auto">During form prepopulation, the conditions are invoked with a <code class="notranslate">FormPrepopulator</code> object implementing <code class="notranslate">FormInterface</code>. When <code class="notranslate">FormPrepopulator::get('field')</code> 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.</p>
<p dir="auto">During form binding, the conditions are invoked with a <code class="notranslate">FormBinder</code> object, that also implements <code class="notranslate">FormInterface</code>. This object works like <code class="notranslate">FormPrepopulator</code>, only that it binds the fields instead of filling them with default data.</p>
<p dir="auto">In both cases, circular dependencies can be detected and reported.</p>