<p dir="auto">Hi @bschussek</p>
<p dir="auto">I'm having an issue since I've updated my code to Symfony v2.3.1, I have an embed form defined as virtual and neither <code class="notranslate">FormEvents::PRE_SET_DATA</code> event nor <code class="notranslate">setData</code> method are called anymore.</p>
<p dir="auto">I wrote a test to be more explicit on the problem I have: <a href="https://gist.github.com/jeremyb/5764427">https://gist.github.com/jeremyb/5764427</a>. I ran this test on the Symfony 2.2 and 2.3 branches and I think there's a regression on <code class="notranslate">testVirtualAndEvents</code>.</p>
<table role="table">
<thead>
<tr>
<th>Test</th>
<th>2.2</th>
<th>2.3</th>
</tr>
</thead>
<tbody>
<tr>
<td>testVirtual</td>
<td>success</td>
<td>success</td>
</tr>
<tr>
<td>testVirtualWithCountry</td>
<td>fails</td>
<td>fails</td>
</tr>
<tr>
<td>testVirtualAndEvents</td>
<td>success</td>
<td>fails</td>
</tr>
</tbody>
</table>
<p dir="auto">I'm trying to understand the differences made on Form component between 2.2 and 2.3 and I see some PR that can be related to this issue:</p>
<ul dir="auto">
<li>I would have thought that PR <a class="issue-link js-issue-link" data-error-text="Failed to load title" data-id="9705925" data-permission-text="Title is private" data-url="https://github.com/symfony/symfony/issues/6573" data-hovercard-type="pull_request" data-hovercard-url="/symfony/symfony/pull/6573/hovercard" href="https://github.com/symfony/symfony/pull/6573">#6573</a> fixes the <code class="notranslate">setData</code> call for virtual forms but it seems that it's not the case and the latest comment of <a class="user-mention notranslate" data-hovercard-type="user" data-hovercard-url="/users/Strate/hovercard" data-octo-click="hovercard-link-click" data-octo-dimensions="link_type:self" href="https://github.com/Strate">@Strate</a> confirms that ;</li>
<li>the PR <a class="issue-link js-issue-link" data-error-text="Failed to load title" data-id="13765895" data-permission-text="Title is private" data-url="https://github.com/symfony/symfony/issues/7878" data-hovercard-type="pull_request" data-hovercard-url="/symfony/symfony/pull/7878/hovercard" href="https://github.com/symfony/symfony/pull/7878">#7878</a> introduces the <code class="notranslate">auto_initialize</code> option, so the <code class="notranslate">setData</code> isn't called anymore on children if I understand it correctly?</li>
</ul>
<p dir="auto">So I think my problem comes from the fact that <code class="notranslate">setData</code> isn't called for virtual forms and because <code class="notranslate">auto_initialize</code> prevents initialization of children (according to this the exception <code class="notranslate">Automatic initialization is only supported on root forms.</code>), so without initialization and data the <code class="notranslate">FormEvents::PRE_SET_DATA</code> event isn't called anymore. Is this correct?</p>
<p dir="auto">I'm not sure whether it's a bug or an evolution of the Form component but is there a new way to do this kind of code and adapt the behavior of a virtual form from the data?</p>
<p dir="auto">Let me know if you want me to commit my <code class="notranslate">FormVirtualTest.php</code> test somewhere, I can do a PR to your symfony fork if you want.</p>
<p dir="auto">Probably in Symfony 2.4 I'm going to make a few changes to the form architecture to enforce a proper form lifecycle. A BC layer will be provided until Symfony 3.0.</p>
<p dir="auto">I'm writing this mainly as instructions to my future me, but anyone interested in the topic is invited to chime in :)</p>
<h4 dir="auto">Summary</h4>
<ul dir="auto">
<li><code class="notranslate">Form::setData()</code> will be deprecated in favor of <code class="notranslate">Form::initialize()</code></li>
<li><code class="notranslate">Form::initialize()</code> can only ever be called once (usually automatically by the form system) guaranteeing the best possible performance</li>
<li><code class="notranslate">Form::initialize()</code> is guaranteed to be called on every element in the form tree (including those with "inherit_data" set), ensuring that all <code class="notranslate">*_SET_DATA</code> events are fired on time</li>
<li>side effects from <code class="notranslate">Form::submit()</code>, <code class="notranslate">getData()</code>, <code class="notranslate">getNormData()</code> and <code class="notranslate">getViewData()</code> will be removed</li>
<li><code class="notranslate">DataMapperInterface</code> will be deprecated in favor of <code class="notranslate">DataMapper2Interface</code> (everybody cheer now ;) )</li>
</ul>
<h4 dir="auto">Question</h4>
<p dir="auto">After you read the text ( ;) ) please help me by answering the following question:</p>
<p dir="auto">Are there use cases where calling <code class="notranslate">Form::setData()</code> is absolutely necessary and cannot be changed so that the default data is set in one of the other ways demonstrated below (last code sample)?</p>
<h4 dir="auto">Motivation</h4>
<h5 dir="auto">Motivation 1: Dynamic Forms</h5>
<p dir="auto">The motivation behind are dynamic forms (<a class="issue-link js-issue-link" data-error-text="Failed to load title" data-id="7759605" data-permission-text="Title is private" data-url="https://github.com/symfony/symfony/issues/5807" data-hovercard-type="issue" data-hovercard-url="/symfony/symfony/issues/5807/hovercard" href="https://github.com/symfony/symfony/issues/5807">#5807</a>). Dynamic forms rely on <code class="notranslate">setData()</code> being called in order to manipulate the form tree dynamically. This must be done before <code class="notranslate">get*Data()</code>, <code class="notranslate">submit()</code> or <code class="notranslate">createView()</code> are called, because these methods rely on the form tree being readily initialized.</p>
<h5 dir="auto">Motivation 2: *_SET_DATA events and "inherit_data"</h5>
<p dir="auto">Currently, forms with the "inherit_data" option set cannot have *_SET_DATA event listeners, because <code class="notranslate">setData()</code> is never called on these forms (<a class="issue-link js-issue-link" data-error-text="Failed to load title" data-id="15447789" data-permission-text="Title is private" data-url="https://github.com/symfony/symfony/issues/8253" data-hovercard-type="issue" data-hovercard-url="/symfony/symfony/issues/8253/hovercard" href="https://github.com/symfony/symfony/issues/8253">#8253</a>, <a class="issue-link js-issue-link" data-error-text="Failed to load title" data-id="17388843" data-permission-text="Title is private" data-url="https://github.com/symfony/symfony/issues/8607" data-hovercard-type="issue" data-hovercard-url="/symfony/symfony/issues/8607/hovercard" href="https://github.com/symfony/symfony/issues/8607">#8607</a>, <a class="issue-link js-issue-link" data-error-text="Failed to load title" data-id="18043823" data-permission-text="Title is private" data-url="https://github.com/symfony/symfony/issues/8748" data-hovercard-type="issue" data-hovercard-url="/symfony/symfony/issues/8748/hovercard" href="https://github.com/symfony/symfony/issues/8748">#8748</a>).</p>
<h5 dir="auto">Motivation 3: Performance</h5>
<p dir="auto">Depending on the number of nested forms and the number of event listeners, <code class="notranslate">setData()</code> can be a very expensive operation. The number of <code class="notranslate">setData()</code> calls were already reduced once in <a class="commit-link" data-hovercard-type="commit" data-hovercard-url="https://github.com/symfony/symfony/commit/d4f4038f6daf7cf88ca7c7ab089473cce5ebf7d8/hovercard" href="https://github.com/symfony/symfony/commit/d4f4038f6daf7cf88ca7c7ab089473cce5ebf7d8"><tt>d4f4038</tt></a> by introducing deferred data initialization. It is still possible to call <code class="notranslate">setData()</code> more than once though. Guaranteeing a single <code class="notranslate">setData()</code> call would also guarantee an optimal performance.</p>
<h5 dir="auto">Motivation 4: Remove side effects</h5>
<p dir="auto">The result of deferred data initialization is that <code class="notranslate">getData()</code>, <code class="notranslate">getNormData()</code>, <code class="notranslate">getViewData()</code> and <code class="notranslate">submit()</code> now have side effects. I would like to remove those.</p>
<h5 dir="auto">Motivation 5: Inline data mapping code</h5>
<p dir="auto">Currently, when writing a custom data mapper, most of the code from PropertyPathMapper has to be duplicated. In fact, the only implementation-dependent lines are</p>
<div class="highlight highlight-text-html-php notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="$form-&gt;setData($this-&gt;propertyAccessor-&gt;getValue($data, $propertyPath));"><pre class="notranslate"><span class="pl-s1"><span class="pl-c1">$</span>form</span>-&gt;<span class="pl-en">setData</span>(<span class="pl-s1"><span class="pl-c1">$</span><span class="pl-smi">this</span></span>-&gt;<span class="pl-c1">propertyAccessor</span>-&gt;<span class="pl-en">getValue</span>(<span class="pl-s1"><span class="pl-c1">$</span>data</span>, <span class="pl-s1"><span class="pl-c1">$</span>propertyPath</span>));</pre></div>
<p dir="auto">and</p>
<div class="highlight highlight-text-html-php notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="$this-&gt;propertyAccessor-&gt;setValue($data, $propertyPath, $form-&gt;getData());"><pre class="notranslate"><span class="pl-s1"><span class="pl-c1">$</span><span class="pl-smi">this</span></span>-&gt;<span class="pl-c1">propertyAccessor</span>-&gt;<span class="pl-en">setValue</span>(<span class="pl-s1"><span class="pl-c1">$</span>data</span>, <span class="pl-s1"><span class="pl-c1">$</span>propertyPath</span>, <span class="pl-s1"><span class="pl-c1">$</span>form</span>-&gt;<span class="pl-en">getData</span>());</pre></div>
<p dir="auto">Even worse, the other code in the data mappers is responsible for maintaining the correct lifecycle of the form. I would like to inline this critical code into <code class="notranslate">Form</code> and provide a new, more minimal data mapper interface.</p>
<h4 dir="auto">Proposed Lifecycle</h4>
<p dir="auto">The proposed form lifecycle will consist of four phases, which partially overlap.</p>
<p dir="auto"><em>Note in advance:</em> I'm talking about the existing low-level API here. Don't confuse it with the high-level API that you typically use in your controllers (types, options, etc.)</p>
<h5 dir="auto">Phase 1: Configuration</h5>
<p dir="auto">In this phase, form builders are created and configured.</p>
<p dir="auto">The phase is complete when a form builder is turned into a form by calling <code class="notranslate">getForm()</code>. At this point, the default data and other configuration of the form are frozen.</p>
<div class="highlight highlight-text-html-php notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="$builder = $factory-&gt;createBuilder()
    -&gt;setData('default')
    -&gt;setDisabled(true)
    -&gt;setCompound(false)
    // ..."><pre class="notranslate"><span class="pl-s1"><span class="pl-c1">$</span>builder</span> = <span class="pl-s1"><span class="pl-c1">$</span>factory</span>-&gt;<span class="pl-en">createBuilder</span>()
    -&gt;<span class="pl-en">setData</span>(<span class="pl-s">'default'</span>)
    -&gt;<span class="pl-en">setDisabled</span>(<span class="pl-c1">true</span>)
    -&gt;<span class="pl-en">setCompound</span>(<span class="pl-c1">false</span>)
    <span class="pl-c">// ...</span></pre></div>
<h5 dir="auto">Phase 2: Assembling</h5>
<p dir="auto">In this phase, the form tree is assembled by connecting the <code class="notranslate">Form</code> instances. Typically, this is done during <code class="notranslate">FormBuilder::getForm()</code>, but in dynamic forms you often need to call <code class="notranslate">Form::add()</code> and <code class="notranslate">Form::remove()</code> manually.</p>
<p dir="auto">This phase is never really complete. The form tree can be changed in the later phases to allow a dynamic modification of the form tree.</p>
<div class="highlight highlight-text-html-php notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="// Configuration
$builder = $factory-&gt;createBuilder()
    -&gt;add('name', 'text')
    -&gt;add('email', 'email');

// Assembling
// Form instances are created, Form::add() is called
$form = $builder-&gt;getForm();

// Manual modifications
$form-&gt;add('submit', 'submit');
$form-&gt;remove('email');"><pre class="notranslate"><span class="pl-c">// Configuration</span>
<span class="pl-s1"><span class="pl-c1">$</span>builder</span> = <span class="pl-s1"><span class="pl-c1">$</span>factory</span>-&gt;<span class="pl-en">createBuilder</span>()
    -&gt;<span class="pl-en">add</span>(<span class="pl-s">'name'</span>, <span class="pl-s">'text'</span>)
    -&gt;<span class="pl-en">add</span>(<span class="pl-s">'email'</span>, <span class="pl-s">'email'</span>);

<span class="pl-c">// Assembling</span>
<span class="pl-c">// Form instances are created, Form::add() is called</span>
<span class="pl-s1"><span class="pl-c1">$</span>form</span> = <span class="pl-s1"><span class="pl-c1">$</span>builder</span>-&gt;<span class="pl-en">getForm</span>();

<span class="pl-c">// Manual modifications</span>
<span class="pl-s1"><span class="pl-c1">$</span>form</span>-&gt;<span class="pl-en">add</span>(<span class="pl-s">'submit'</span>, <span class="pl-s">'submit'</span>);
<span class="pl-s1"><span class="pl-c1">$</span>form</span>-&gt;<span class="pl-en">remove</span>(<span class="pl-s">'email'</span>);</pre></div>
<h5 dir="auto">Phase 3: Initialization</h5>
<p dir="auto">During initialization, the form tree is populated with default values. Initialization starts when <code class="notranslate">Form::initialize($defaultData = null)</code> is called on the root form. The call will cascade through the form tree.</p>
<p dir="auto">After the call is complete, <code class="notranslate">initialize()</code> is guaranteed to have been called on every element in the form tree. If elements are added later on, <code class="notranslate">initialize()</code> will be called on them during <code class="notranslate">Form::add()</code>.</p>
<p dir="auto"><code class="notranslate">initialize()</code> is very similar to the current <code class="notranslate">setData()</code>, i.e. the form's data is set and <code class="notranslate">*_SET_DATA</code> events are fired. Consequently, after the initialization of a form tree, all <code class="notranslate">*_SET_DATA</code> events are fired and their effects visible.</p>
<p dir="auto">There are three major differences between <code class="notranslate">initialize()</code> and the current <code class="notranslate">setData()</code>:</p>
<p dir="auto">First, <code class="notranslate">initialize()</code> must <strong>only be called once</strong>. Since <code class="notranslate">initialize()</code> cascades through the form tree, this implies that <code class="notranslate">initialize()</code> must only be called on the root form.</p>
<p dir="auto">Second, the data argument in can be omitted. This happens mostly for the root form and when "mapped" is set to <code class="notranslate">false</code>. In this case, the default data will be retrieved from the form's configuration, which has been created in Phase 1.</p>
<div class="highlight highlight-text-html-php notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="$form = $factory-&gt;createBuilder('form', null, array('auto_initialize' =&gt; false))
    -&gt;add('name', 'text')
    -&gt;add('email', 'email')
    // passing the data as second argument to
    // FormFactory::createBuilder() or through the option
    // &quot;data&quot; is equivalent to the below statement
    -&gt;setData(array('name' =&gt; 'foo', 'email' =&gt; 'bar'))
    -&gt;getForm();

$form-&gt;initialize();
// sets the default data stored above

// implies $form-&gt;get('name')-&gt;initialize('foo')
// implies $form-&gt;get('email')-&gt;initialize('bar')"><pre class="notranslate"><span class="pl-s1"><span class="pl-c1">$</span>form</span> = <span class="pl-s1"><span class="pl-c1">$</span>factory</span>-&gt;<span class="pl-en">createBuilder</span>(<span class="pl-s">'form'</span>, <span class="pl-c1">null</span>, <span class="pl-en">array</span>(<span class="pl-s">'auto_initialize'</span> =&gt; <span class="pl-c1">false</span>))
    -&gt;<span class="pl-en">add</span>(<span class="pl-s">'name'</span>, <span class="pl-s">'text'</span>)
    -&gt;<span class="pl-en">add</span>(<span class="pl-s">'email'</span>, <span class="pl-s">'email'</span>)
    <span class="pl-c">// passing the data as second argument to</span>
    <span class="pl-c">// FormFactory::createBuilder() or through the option</span>
    <span class="pl-c">// "data" is equivalent to the below statement</span>
    -&gt;<span class="pl-en">setData</span>(<span class="pl-en">array</span>(<span class="pl-s">'name'</span> =&gt; <span class="pl-s">'foo'</span>, <span class="pl-s">'email'</span> =&gt; <span class="pl-s">'bar'</span>))
    -&gt;<span class="pl-en">getForm</span>();

<span class="pl-s1"><span class="pl-c1">$</span>form</span>-&gt;<span class="pl-en">initialize</span>();
<span class="pl-c">// sets the default data stored above</span>

<span class="pl-c">// implies $form-&gt;get('name')-&gt;initialize('foo')</span>
<span class="pl-c">// implies $form-&gt;get('email')-&gt;initialize('bar')</span></pre></div>
<p dir="auto">If the "auto_initialize" option is not set to <code class="notranslate">false</code>, as in this example, <code class="notranslate">initialize()</code> will be called automatically (this is already happening right now for your Symfony 2.3 forms).</p>
<p dir="auto">Third and last, contrary to the current <code class="notranslate">setData()</code>, initialized forms cannot be added as children of other forms anymore.</p>
<div class="highlight highlight-text-html-php notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="$form = $factory-&gt;createForm('text', null, array('auto_initialize' =&gt; false));
$form-&gt;initialize('foo');

$otherForm-&gt;add($form); // boom

$form = $factory-&gt;createForm('text');

$otherForm-&gt;add($form); // boom, auto initialized"><pre class="notranslate"><span class="pl-s1"><span class="pl-c1">$</span>form</span> = <span class="pl-s1"><span class="pl-c1">$</span>factory</span>-&gt;<span class="pl-en">createForm</span>(<span class="pl-s">'text'</span>, <span class="pl-c1">null</span>, <span class="pl-en">array</span>(<span class="pl-s">'auto_initialize'</span> =&gt; <span class="pl-c1">false</span>));
<span class="pl-s1"><span class="pl-c1">$</span>form</span>-&gt;<span class="pl-en">initialize</span>(<span class="pl-s">'foo'</span>);

<span class="pl-s1"><span class="pl-c1">$</span>otherForm</span>-&gt;<span class="pl-en">add</span>(<span class="pl-s1"><span class="pl-c1">$</span>form</span>); <span class="pl-c">// boom</span>

<span class="pl-s1"><span class="pl-c1">$</span>form</span> = <span class="pl-s1"><span class="pl-c1">$</span>factory</span>-&gt;<span class="pl-en">createForm</span>(<span class="pl-s">'text'</span>);

<span class="pl-s1"><span class="pl-c1">$</span>otherForm</span>-&gt;<span class="pl-en">add</span>(<span class="pl-s1"><span class="pl-c1">$</span>form</span>); <span class="pl-c">// boom, auto initialized</span></pre></div>
<h5 dir="auto">Phase 4: Submission</h5>
<p dir="auto">After a form tree is readily initialized and all <code class="notranslate">*_SET_DATA</code> events triggered, the form can be submitted. Forms must not be submitted before initialization is complete.</p>
<div class="highlight highlight-text-html-php notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="// Phase 1 and 2 inside the form type
// Phase 3 due to automatic initialization
$form = $this-&gt;createForm('my_type');

// Phase 4
$form-&gt;handleRequest($request);

if ($form-&gt;isSubmitted() &amp;&amp; $form-&gt;isValid()) {
    // ...
}"><pre class="notranslate"><span class="pl-c">// Phase 1 and 2 inside the form type</span>
<span class="pl-c">// Phase 3 due to automatic initialization</span>
<span class="pl-s1"><span class="pl-c1">$</span>form</span> = <span class="pl-s1"><span class="pl-c1">$</span><span class="pl-smi">this</span></span>-&gt;<span class="pl-en">createForm</span>(<span class="pl-s">'my_type'</span>);

<span class="pl-c">// Phase 4</span>
<span class="pl-s1"><span class="pl-c1">$</span>form</span>-&gt;<span class="pl-en">handleRequest</span>(<span class="pl-s1"><span class="pl-c1">$</span>request</span>);

<span class="pl-k">if</span> (<span class="pl-s1"><span class="pl-c1">$</span>form</span>-&gt;<span class="pl-en">isSubmitted</span>() &amp;&amp; <span class="pl-s1"><span class="pl-c1">$</span>form</span>-&gt;<span class="pl-en">isValid</span>()) {
    <span class="pl-c">// ...</span>
}</pre></div>
<p dir="auto">The only difference will be that there will most likely be an "auto_submit" option with a default of <code class="notranslate">true</code>, sparing you of calling <code class="notranslate">handleRequest()</code> manually.</p>
<div class="highlight highlight-text-html-php notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="// Phase 1 and 2 inside the form type
// Phase 3 due to automatic initialization
// Phase 4 due to automatic submission
$form = $this-&gt;createForm('my_type');

if ($form-&gt;isSubmitted() &amp;&amp; $form-&gt;isValid()) {
    // ...
}"><pre class="notranslate"><span class="pl-c">// Phase 1 and 2 inside the form type</span>
<span class="pl-c">// Phase 3 due to automatic initialization</span>
<span class="pl-c">// Phase 4 due to automatic submission</span>
<span class="pl-s1"><span class="pl-c1">$</span>form</span> = <span class="pl-s1"><span class="pl-c1">$</span><span class="pl-smi">this</span></span>-&gt;<span class="pl-en">createForm</span>(<span class="pl-s">'my_type'</span>);

<span class="pl-k">if</span> (<span class="pl-s1"><span class="pl-c1">$</span>form</span>-&gt;<span class="pl-en">isSubmitted</span>() &amp;&amp; <span class="pl-s1"><span class="pl-c1">$</span>form</span>-&gt;<span class="pl-en">isValid</span>()) {
    <span class="pl-c">// ...</span>
}</pre></div>
<h4 dir="auto">Implementation</h4>
<p dir="auto">A few details on what changes are necessary to make the above happen.</p>
<h5 dir="auto">Introduce new DataMapperInterface</h5>
<p dir="auto">A new data mapper interface will be introduced. Due to BC, it will have a very uninspring name:</p>
<div class="highlight highlight-text-html-php notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="interface DataMapper2Interface
{
    /**
     * Returns true if the data is considered empty. The
     * preconfigured default value will be used in this case.
     *
     * @param mixed $data
     *
     * @return Boolean
     */
    public function isEmpty($data);

    /**
     * Returns whether the data can be read. For example,
     * an ArrayAccessMapper will return true if and only if
     * $data is an array.
     *
     * @param mixed $data
     *
     * @return Boolean
     */
    public function isReadable($data);

    /**
     * Returns a textual representation of the types that
     * this mapper can read, which will be included in the
     * error message generated if isReadable() returns false.
     *
     * @return string
     */
    public function getReadableTypes();

    /**
     * Returns the mapped value from $data by accessing $path.
     * $path can be anything, depending on the implementation
     * (e.g. property path, XPath, array index etc.)
     *
     * @param mixed $data
     * @param mixed $path
     *
     * @return mixed The value read from $data
     */
    public function readValue($data, $path);

    /**
     * Writes a value into $data.
     *
     * @param mixed $data
     * @param mixed $path
     * @param mixed $value
     */
    public function writeValue($data, $path, $value)
}"><pre class="notranslate"><span class="pl-k">interface</span> <span class="pl-v">DataMapper2Interface</span>
{
    <span class="pl-c">/**</span>
<span class="pl-c">     * Returns true if the data is considered empty. The</span>
<span class="pl-c">     * preconfigured default value will be used in this case.</span>
<span class="pl-c">     *</span>
<span class="pl-c">     * @param mixed $data</span>
<span class="pl-c">     *</span>
<span class="pl-c">     * @return Boolean</span>
<span class="pl-c">     */</span>
    <span class="pl-k">public</span> <span class="pl-k">function</span> <span class="pl-en">isEmpty</span>(<span class="pl-s1"><span class="pl-c1">$</span>data</span>);

    <span class="pl-c">/**</span>
<span class="pl-c">     * Returns whether the data can be read. For example,</span>
<span class="pl-c">     * an ArrayAccessMapper will return true if and only if</span>
<span class="pl-c">     * $data is an array.</span>
<span class="pl-c">     *</span>
<span class="pl-c">     * @param mixed $data</span>
<span class="pl-c">     *</span>
<span class="pl-c">     * @return Boolean</span>
<span class="pl-c">     */</span>
    <span class="pl-k">public</span> <span class="pl-k">function</span> <span class="pl-en">isReadable</span>(<span class="pl-s1"><span class="pl-c1">$</span>data</span>);

    <span class="pl-c">/**</span>
<span class="pl-c">     * Returns a textual representation of the types that</span>
<span class="pl-c">     * this mapper can read, which will be included in the</span>
<span class="pl-c">     * error message generated if isReadable() returns false.</span>
<span class="pl-c">     *</span>
<span class="pl-c">     * @return string</span>
<span class="pl-c">     */</span>
    <span class="pl-k">public</span> <span class="pl-k">function</span> <span class="pl-en">getReadableTypes</span>();

    <span class="pl-c">/**</span>
<span class="pl-c">     * Returns the mapped value from $data by accessing $path.</span>
<span class="pl-c">     * $path can be anything, depending on the implementation</span>
<span class="pl-c">     * (e.g. property path, XPath, array index etc.)</span>
<span class="pl-c">     *</span>
<span class="pl-c">     * @param mixed $data</span>
<span class="pl-c">     * @param mixed $path</span>
<span class="pl-c">     *</span>
<span class="pl-c">     * @return mixed The value read from $data</span>
<span class="pl-c">     */</span>
    <span class="pl-k">public</span> <span class="pl-k">function</span> <span class="pl-en">readValue</span>(<span class="pl-s1"><span class="pl-c1">$</span>data</span>, <span class="pl-s1"><span class="pl-c1">$</span>path</span>);

    <span class="pl-c">/**</span>
<span class="pl-c">     * Writes a value into $data.</span>
<span class="pl-c">     *</span>
<span class="pl-c">     * @param mixed $data</span>
<span class="pl-c">     * @param mixed $path</span>
<span class="pl-c">     * @param mixed $value</span>
<span class="pl-c">     */</span>
    <span class="pl-k">public</span> <span class="pl-k">function</span> <span class="pl-en">writeValue</span>(<span class="pl-s1"><span class="pl-c1">$</span>data</span>, <span class="pl-s1"><span class="pl-c1">$</span>path</span>, <span class="pl-s1"><span class="pl-c1">$</span>value</span>)
}</pre></div>
<p dir="auto">This mapper can be used <em>alternatively</em> to an implementation of the current <code class="notranslate">DataMapperInterface</code>. The other code contained in current <code class="notranslate">DataMapperInterface</code> implementation will be executed by <code class="notranslate">Form</code> directly (see Motivation 5).</p>
<h5 dir="auto">Call initialize() on forms with "inherit_data" true</h5>
<p dir="auto">Once the data mapping code is inlined into <code class="notranslate">Form</code>, <code class="notranslate">initialize()</code> can also be called on forms with the "inherit_data" option set (see Motivation 2). This is currently impossible, because the current data mappers only have access to the parent's view data, but need to pass the parent's model data instead.</p>
<h5 dir="auto">Remove side effects</h5>
<p dir="auto">The side effects from <code class="notranslate">getData()</code>, <code class="notranslate">getNormData()</code>,<br>
<code class="notranslate">getViewData()</code> and <code class="notranslate">submit()</code> should be removed. Instead, exceptions should be thrown.</p>
<div class="highlight highlight-text-html-php notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="// old
if (!$this-&gt;defaultDataSet) {
    $this-&gt;setData($this-&gt;getConfig()-&gt;getData());
}

// new
if (!$this-&gt;defaultDataSet) {
    throw new RuntimeException('getData() must be called after initializing the form. Make sure &quot;auto_initialize&quot; is not disabled on the root form.');
}"><pre class="notranslate"><span class="pl-c">// old</span>
<span class="pl-k">if</span> (!<span class="pl-s1"><span class="pl-c1">$</span><span class="pl-smi">this</span></span>-&gt;<span class="pl-c1">defaultDataSet</span>) {
    <span class="pl-s1"><span class="pl-c1">$</span><span class="pl-smi">this</span></span>-&gt;<span class="pl-en">setData</span>(<span class="pl-s1"><span class="pl-c1">$</span><span class="pl-smi">this</span></span>-&gt;<span class="pl-en">getConfig</span>()-&gt;<span class="pl-en">getData</span>());
}

<span class="pl-c">// new</span>
<span class="pl-k">if</span> (!<span class="pl-s1"><span class="pl-c1">$</span><span class="pl-smi">this</span></span>-&gt;<span class="pl-c1">defaultDataSet</span>) {
    <span class="pl-k">throw</span> <span class="pl-k">new</span> <span class="pl-v">RuntimeException</span>(<span class="pl-s">'getData() must be called after initializing the form. Make sure "auto_initialize" is not disabled on the root form.'</span>);
}</pre></div>
<p dir="auto">This is possible, because <code class="notranslate">initialize()</code>, which is guaranteed to be called, also guarantees to fill the form with the appropriate default data.</p>
<h5 dir="auto">Deprecate setData()</h5>
<p dir="auto">As last step, <code class="notranslate">setData()</code> will be deprecated. Consequently, you should only set default data during the configuration phase by one of the already existing means:</p>
<div class="highlight highlight-text-html-php notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="// passed to the factory
$form = $factory-&gt;create('form', $default);

// set on the builder
$form = $factory-&gt;createBuilder('form')
    -&gt;setData($default)
    -&gt;getForm();

// passed as option
$form = $factory-&gt;create('form', null, array('data' =&gt; $default));
$form-&gt;add('field', 'text', array('data' =&gt; $default));

// deprecated
$form-&gt;setData($default);"><pre class="notranslate"><span class="pl-c">// passed to the factory</span>
<span class="pl-s1"><span class="pl-c1">$</span>form</span> = <span class="pl-s1"><span class="pl-c1">$</span>factory</span>-&gt;<span class="pl-en">create</span>(<span class="pl-s">'form'</span>, <span class="pl-s1"><span class="pl-c1">$</span>default</span>);

<span class="pl-c">// set on the builder</span>
<span class="pl-s1"><span class="pl-c1">$</span>form</span> = <span class="pl-s1"><span class="pl-c1">$</span>factory</span>-&gt;<span class="pl-en">createBuilder</span>(<span class="pl-s">'form'</span>)
    -&gt;<span class="pl-en">setData</span>(<span class="pl-s1"><span class="pl-c1">$</span>default</span>)
    -&gt;<span class="pl-en">getForm</span>();

<span class="pl-c">// passed as option</span>
<span class="pl-s1"><span class="pl-c1">$</span>form</span> = <span class="pl-s1"><span class="pl-c1">$</span>factory</span>-&gt;<span class="pl-en">create</span>(<span class="pl-s">'form'</span>, <span class="pl-c1">null</span>, <span class="pl-en">array</span>(<span class="pl-s">'data'</span> =&gt; <span class="pl-s1"><span class="pl-c1">$</span>default</span>));
<span class="pl-s1"><span class="pl-c1">$</span>form</span>-&gt;<span class="pl-en">add</span>(<span class="pl-s">'field'</span>, <span class="pl-s">'text'</span>, <span class="pl-en">array</span>(<span class="pl-s">'data'</span> =&gt; <span class="pl-s1"><span class="pl-c1">$</span>default</span>));

<span class="pl-c">// deprecated</span>
<span class="pl-s1"><span class="pl-c1">$</span>form</span>-&gt;<span class="pl-en">setData</span>(<span class="pl-s1"><span class="pl-c1">$</span>default</span>);</pre></div>
<p dir="auto">If you made it up to here, you have my full respect :) Please don' forget to answer my question above.</p>