<p dir="auto">I was looking at the templating helper classes in FrameworkBundle and SecurityBundle. There appears to be a fair about of code duplication between these classes and their Twig extension counterparts.</p>
<p dir="auto">I realize helpers extend a base class, as do Twig extensions, so separate classes must continue to exist; however, we could use object composition to have the Twig extensions expose the helper's methods to Twig. In some cases, this might require an intermediary wrapping method in the helper.</p>
<p dir="auto">(Copied from <a class="issue-link js-issue-link" data-error-text="Failed to load title" data-id="9584876" data-permission-text="Title is private" data-url="https://github.com/symfony/symfony/issues/6522" data-hovercard-type="pull_request" data-hovercard-url="/symfony/symfony/pull/6522/hovercard" href="https://github.com/symfony/symfony/pull/6522">#6522</a>)</p>
<p dir="auto">With the current implementation, the attributes passed in the <code class="notranslate">attr</code> option are rendered both in the <code class="notranslate">&lt;form&gt;</code> tag (new) and in the <code class="notranslate">&lt;div&gt;</code>/<code class="notranslate">&lt;table&gt;</code> tag of the form (for BC).</p>
<div class="highlight highlight-text-html-php notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="$form = $this-&gt;createForm('my_form', $formData, array(
    'attr' =&gt; array(
        'id' =&gt; 'foo',
        'class' =&gt; 'bar',
    ),
));"><pre class="notranslate"><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_form'</span>, <span class="pl-s1"><span class="pl-c1">$</span>formData</span>, <span class="pl-en">array</span>(
    <span class="pl-s">'attr'</span> =&gt; <span class="pl-en">array</span>(
        <span class="pl-s">'id'</span> =&gt; <span class="pl-s">'foo'</span>,
        <span class="pl-s">'class'</span> =&gt; <span class="pl-s">'bar'</span>,
    ),
));</pre></div>
<p dir="auto">Template:</p>
<div class="highlight highlight-text-html-basic notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="{{ form(form) }}

-&gt;

&lt;form id=&quot;foo&quot; class=&quot;bar&quot;&gt;
    &lt;div id=&quot;foo&quot; class=&quot;bar&quot;&gt;
        &lt;!-- rows --&gt;
    &lt;/div&gt;
&lt;/form&gt;"><pre class="notranslate">{{ form(form) }}

-<span class="pl-kos">&gt;</span>

<span class="pl-kos">&lt;</span><span class="pl-ent">form</span> <span class="pl-c1">id</span>="<span class="pl-s">foo</span>" <span class="pl-c1">class</span>="<span class="pl-s">bar</span>"<span class="pl-kos">&gt;</span>
    <span class="pl-kos">&lt;</span><span class="pl-ent">div</span> <span class="pl-c1">id</span>="<span class="pl-s">foo</span>" <span class="pl-c1">class</span>="<span class="pl-s">bar</span>"<span class="pl-kos">&gt;</span>
        <span class="pl-c">&lt;!-- rows --&gt;</span>
    <span class="pl-kos">&lt;/</span><span class="pl-ent">div</span><span class="pl-kos">&gt;</span>
<span class="pl-kos">&lt;/</span><span class="pl-ent">form</span><span class="pl-kos">&gt;</span></pre></div>
<p dir="auto">Solutions that come to mind:</p>
<p dir="auto">(a) Move the rendering of the attributes from the <code class="notranslate">&lt;div&gt;</code>/<code class="notranslate">&lt;table&gt;</code> tag to the <code class="notranslate">&lt;form&gt;</code> tag <strong>for the root form</strong>. This breaks BC because the <code class="notranslate">form_widget()</code> call for the root form does not output the attributes anymore then. Nested forms are not affected.</p>
<div class="highlight highlight-text-html-basic notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="&lt;form id=&quot;foo&quot; class=&quot;bar&quot;&gt;
    &lt;div&gt;&lt;!-- no attrs! --&gt;
        &lt;!-- rows --&gt;
    &lt;/div&gt;
&lt;/form&gt;"><pre class="notranslate"><span class="pl-kos">&lt;</span><span class="pl-ent">form</span> <span class="pl-c1">id</span>="<span class="pl-s">foo</span>" <span class="pl-c1">class</span>="<span class="pl-s">bar</span>"<span class="pl-kos">&gt;</span>
    <span class="pl-kos">&lt;</span><span class="pl-ent">div</span><span class="pl-kos">&gt;</span><span class="pl-c">&lt;!-- no attrs! --&gt;</span>
        <span class="pl-c">&lt;!-- rows --&gt;</span>
    <span class="pl-kos">&lt;/</span><span class="pl-ent">div</span><span class="pl-kos">&gt;</span>
<span class="pl-kos">&lt;/</span><span class="pl-ent">form</span><span class="pl-kos">&gt;</span></pre></div>
<p dir="auto">(b) Don't render a <code class="notranslate">&lt;div&gt;</code> tag <strong>for the root form</strong> and do as in (a) for the table layout. This obviously also breaks BC. Nested forms are not affected.</p>
<div class="highlight highlight-text-html-basic notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="&lt;form id=&quot;foo&quot; class=&quot;bar&quot;&gt;
    &lt;!-- rows --&gt;
&lt;/form&gt;"><pre class="notranslate"><span class="pl-kos">&lt;</span><span class="pl-ent">form</span> <span class="pl-c1">id</span>="<span class="pl-s">foo</span>" <span class="pl-c1">class</span>="<span class="pl-s">bar</span>"<span class="pl-kos">&gt;</span>
    <span class="pl-c">&lt;!-- rows --&gt;</span>
<span class="pl-kos">&lt;/</span><span class="pl-ent">form</span><span class="pl-kos">&gt;</span></pre></div>
<p dir="auto">(c) Don't render any attributes in the <code class="notranslate">&lt;form&gt;</code> tag. Maintains full BC.</p>
<div class="highlight highlight-text-html-basic notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="&lt;form&gt;
    &lt;div id=&quot;foo&quot; class=&quot;bar&quot;&gt;
        &lt;!-- rows --&gt;
    &lt;/div&gt;
&lt;/form&gt;"><pre class="notranslate"><span class="pl-kos">&lt;</span><span class="pl-ent">form</span><span class="pl-kos">&gt;</span>
    <span class="pl-kos">&lt;</span><span class="pl-ent">div</span> <span class="pl-c1">id</span>="<span class="pl-s">foo</span>" <span class="pl-c1">class</span>="<span class="pl-s">bar</span>"<span class="pl-kos">&gt;</span>
        <span class="pl-c">&lt;!-- rows --&gt;</span>
    <span class="pl-kos">&lt;/</span><span class="pl-ent">div</span><span class="pl-kos">&gt;</span>
<span class="pl-kos">&lt;/</span><span class="pl-ent">form</span><span class="pl-kos">&gt;</span></pre></div>
<p dir="auto">Opinions?</p>
<p dir="auto">While (c) is the best solution from a BC point of view, I think it is rather unintuitive. As a newcomer, if I put a "class" attribute on a form, I would expect it to be on the outermost <code class="notranslate">&lt;form&gt;</code> tag, not on some <code class="notranslate">&lt;div&gt;</code> within.</p>
<p dir="auto">(a) and (b), on the other hand, break BC for those people that use <code class="notranslate">form_widget()</code> to render the complete form. I don't know how many people would be affected by this, <a href="http://twtpoll.com/p3y7vb" rel="nofollow">this poll</a> tries to gather some data about that. IMO both (a) and (b) are more intuitive and future-proof.</p>
<p dir="auto">My favorite is (b) because it removes a useless <code class="notranslate">&lt;div&gt;</code> from the default markup.</p>