<p dir="auto">Please add standarized/easy way to implement dependent selects form field..</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>