<p dir="auto">Hi,</p>
<p dir="auto">I think I may have found a bug in ValidatorTypeGuesser , either that or this is quite an unexpected behavior.</p>
<p dir="auto">Validator guesser for required (guessRequired) does not seem to work properly when there is no constraint defined for a given property at all.<br>
The documentation says: "The required option can be guessed based on the validation rules (i.e. is the field NotBlank or NotNull)"<br>
Which seems to be confirmed by the source code comments in ValidatorTypeGuesser.php:</p>
<div class="highlight highlight-text-html-php notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="// If we don't find any constraint telling otherwise, we can assume
// that a field is not required (with LOW_CONFIDENCE)"><pre class="notranslate"><span class="pl-c">// If we don't find any constraint telling otherwise, we can assume</span>
<span class="pl-c">// that a field is not required (with LOW_CONFIDENCE)</span></pre></div>
<p dir="auto">however this is not happening if no constraints are defined at all, it does work properly when a non-related constraint is defined (any constraint)</p>
<p dir="auto">to reproduce:</p>
<p dir="auto">The form:</p>
<div class="highlight highlight-text-html-php notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="class TestFormType extends AbstractType 
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            -&gt;add(&quot;name&quot;)
            -&gt;add(&quot;comments&quot;);
    }
    public function getName()
    {
        return &quot;test_form&quot;;
    }
}"><pre class="notranslate"><span class="pl-k">class</span> <span class="pl-v">TestFormType</span> <span class="pl-k">extends</span> <span class="pl-v">AbstractType</span> 
{
    <span class="pl-k">public</span> <span class="pl-k">function</span> <span class="pl-en">buildForm</span>(<span class="pl-smi"><span class="pl-smi">FormBuilderInterface</span></span> <span class="pl-s1"><span class="pl-c1">$</span>builder</span>, <span class="pl-smi">array</span> <span class="pl-s1"><span class="pl-c1">$</span>options</span>)
    {
        <span class="pl-s1"><span class="pl-c1">$</span>builder</span>
            -&gt;<span class="pl-en">add</span>("<span class="pl-s">name</span>")
            -&gt;<span class="pl-en">add</span>("<span class="pl-s">comments</span>");
    }
    <span class="pl-k">public</span> <span class="pl-k">function</span> <span class="pl-en">getName</span>()
    {
        <span class="pl-k">return</span> "<span class="pl-s">test_form</span>";
    }
}</pre></div>
<p dir="auto">The entity:</p>
<div class="highlight highlight-text-html-php notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="class TestEntity
{
    private $name;
    private $comments;

// ... getters and setters ...
}"><pre class="notranslate"><span class="pl-k">class</span> <span class="pl-v">TestEntity</span>
{
    <span class="pl-k">private</span> <span class="pl-c1"><span class="pl-c1">$</span>name</span>;
    <span class="pl-k">private</span> <span class="pl-c1"><span class="pl-c1">$</span>comments</span>;

<span class="pl-c">// ... getters and setters ...</span>
}</pre></div>
<p dir="auto">The validator:</p>
<div class="highlight highlight-text-xml notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="&lt;class name=&quot;FormExperiment\TestBundle\Entity\TestEntity&quot;&gt;
        &lt;property name=&quot;name&quot;&gt;
            &lt;constraint name=&quot;NotBlank&quot; /&gt;
        &lt;/property&gt;
    &lt;/class&gt;"><pre class="notranslate">&lt;<span class="pl-ent">class</span> <span class="pl-e">name</span>=<span class="pl-s"><span class="pl-pds">"</span>FormExperiment\TestBundle\Entity\TestEntity<span class="pl-pds">"</span></span>&gt;
        &lt;<span class="pl-ent">property</span> <span class="pl-e">name</span>=<span class="pl-s"><span class="pl-pds">"</span>name<span class="pl-pds">"</span></span>&gt;
            &lt;<span class="pl-ent">constraint</span> <span class="pl-e">name</span>=<span class="pl-s"><span class="pl-pds">"</span>NotBlank<span class="pl-pds">"</span></span> /&gt;
        &lt;/<span class="pl-ent">property</span>&gt;
    &lt;/<span class="pl-ent">class</span>&gt;</pre></div>
<p dir="auto">and in the controller simply:</p>
<div class="highlight highlight-text-html-php notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="$form = $this-&gt;createForm(new TestFormType(), new TestEntity());"><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-k">new</span> <span class="pl-v">TestFormType</span>(), <span class="pl-k">new</span> <span class="pl-v">TestEntity</span>());</pre></div>
<p dir="auto">and then just pass <code class="notranslate">$form-&gt;createView()</code> to the template</p>
<p dir="auto">Expected behavior:<br>
<code class="notranslate">name</code> has <code class="notranslate">required="required"</code>, and comments doesn't have it.</p>
<p dir="auto">What actually happens:<br>
Both <code class="notranslate">name</code> and <code class="notranslate">comments</code> have <code class="notranslate">required="required"</code></p>
<p dir="auto">If you add any constraint to 'comments', like:</p>
<div class="highlight highlight-text-html-php notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="        &lt;property name=&quot;comments&quot;&gt;
            &lt;constraint name=&quot;Length&quot;&gt;
                &lt;option name=&quot;max&quot;&gt;5&lt;/option&gt;
            &lt;/constraint&gt;
        &lt;/property&gt;"><pre class="notranslate">        &lt;property name="<span class="pl-s">comments</span>"&gt;
            &lt;constraint name="<span class="pl-s">Length</span>"&gt;
                &lt;option name="<span class="pl-s">max</span>"&gt;<span class="pl-c1">5</span>&lt;/option&gt;
            &lt;/constraint&gt;
        &lt;/property&gt;</pre></div>
<p dir="auto">then it will guess the required option properly (in this case required is false since the constraint is not NotNull or NotBlank)</p>
<p dir="auto">even more bizarre that if I override the default in the form type with:</p>
<div class="highlight highlight-text-html-php notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="public function setDefaultOptions(OptionsResolverInterface $resolver)
    {
        $resolver-&gt;setDefaults(array(
                'required' =&gt; false
            ));
    }"><pre class="notranslate"><span class="pl-k">public</span> <span class="pl-k">function</span> <span class="pl-en">setDefaultOptions</span>(<span class="pl-smi"><span class="pl-smi">OptionsResolverInterface</span></span> <span class="pl-s1"><span class="pl-c1">$</span>resolver</span>)
    {
        <span class="pl-s1"><span class="pl-c1">$</span>resolver</span>-&gt;<span class="pl-en">setDefaults</span>(<span class="pl-en">array</span>(
                <span class="pl-s">'required'</span> =&gt; <span class="pl-c1">false</span>
            ));
    }</pre></div>
<p dir="auto">then both will miss <code class="notranslate">required="required"</code> regardless of the guessing altogether, seems like the default option is stronger than anything else.</p>
<p dir="auto">I managed to trace the problem and make the guesser work though:<br>
The main problem is that since constraints are not present for comments at all the whole guessing is skipped here (ValidatorTypeGuesser.php):</p>
<div class="highlight highlight-text-html-php notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="    protected function guess($class, $property, \Closure $closure, $defaultValue = null)
    {
        $guesses = array();
        $classMetadata = $this-&gt;metadataFactory-&gt;getMetadataFor($class);

       if ($classMetadata-&gt;hasMemberMetadatas($property)) {
            $memberMetadatas = $classMetadata-&gt;getMemberMetadatas($property);

            foreach ($memberMetadatas as $memberMetadata) {
                $constraints = $memberMetadata-&gt;getConstraints();

                foreach ($constraints as $constraint) {
                    if ($guess = $closure($constraint)) {
                        $guesses[] = $guess;
                    }
                }
            }

            if (null !== $defaultValue) {
                $guesses[] = new ValueGuess($defaultValue, Guess::LOW_CONFIDENCE);
            }
        }

        return Guess::getBestGuess($guesses);
    }"><pre class="notranslate">    <span class="pl-k">protected</span> <span class="pl-k">function</span> <span class="pl-en">guess</span>(<span class="pl-s1"><span class="pl-c1">$</span>class</span>, <span class="pl-s1"><span class="pl-c1">$</span>property</span>, <span class="pl-smi"><span class="pl-smi">\<span class="pl-v">Closure</span></span></span> <span class="pl-s1"><span class="pl-c1">$</span>closure</span>, <span class="pl-s1"><span class="pl-c1">$</span>defaultValue</span> = <span class="pl-c1">null</span>)
    {
        <span class="pl-s1"><span class="pl-c1">$</span>guesses</span> = <span class="pl-en">array</span>();
        <span class="pl-s1"><span class="pl-c1">$</span>classMetadata</span> = <span class="pl-s1"><span class="pl-c1">$</span><span class="pl-smi">this</span></span>-&gt;<span class="pl-c1">metadataFactory</span>-&gt;<span class="pl-en">getMetadataFor</span>(<span class="pl-s1"><span class="pl-c1">$</span>class</span>);

       <span class="pl-k">if</span> (<span class="pl-s1"><span class="pl-c1">$</span>classMetadata</span>-&gt;<span class="pl-en">hasMemberMetadatas</span>(<span class="pl-s1"><span class="pl-c1">$</span>property</span>)) {
            <span class="pl-s1"><span class="pl-c1">$</span>memberMetadatas</span> = <span class="pl-s1"><span class="pl-c1">$</span>classMetadata</span>-&gt;<span class="pl-en">getMemberMetadatas</span>(<span class="pl-s1"><span class="pl-c1">$</span>property</span>);

            <span class="pl-k">foreach</span> (<span class="pl-s1"><span class="pl-c1">$</span>memberMetadatas</span> <span class="pl-k">as</span> <span class="pl-s1"><span class="pl-c1">$</span>memberMetadata</span>) {
                <span class="pl-s1"><span class="pl-c1">$</span>constraints</span> = <span class="pl-s1"><span class="pl-c1">$</span>memberMetadata</span>-&gt;<span class="pl-en">getConstraints</span>();

                <span class="pl-k">foreach</span> (<span class="pl-s1"><span class="pl-c1">$</span>constraints</span> <span class="pl-k">as</span> <span class="pl-s1"><span class="pl-c1">$</span>constraint</span>) {
                    <span class="pl-k">if</span> (<span class="pl-s1"><span class="pl-c1">$</span>guess</span> = <span class="pl-s1"><span class="pl-c1">$</span>closure</span>(<span class="pl-s1"><span class="pl-c1">$</span>constraint</span>)) {
                        <span class="pl-s1"><span class="pl-c1">$</span>guesses</span>[] = <span class="pl-s1"><span class="pl-c1">$</span>guess</span>;
                    }
                }
            }

            <span class="pl-k">if</span> (<span class="pl-c1">null</span> !== <span class="pl-s1"><span class="pl-c1">$</span>defaultValue</span>) {
                <span class="pl-s1"><span class="pl-c1">$</span>guesses</span>[] = <span class="pl-k">new</span> <span class="pl-v">ValueGuess</span>(<span class="pl-s1"><span class="pl-c1">$</span>defaultValue</span>, <span class="pl-v">Guess</span>::<span class="pl-c1">LOW_CONFIDENCE</span>);
            }
        }

        <span class="pl-k">return</span> <span class="pl-v">Guess</span>::<span class="pl-en">getBestGuess</span>(<span class="pl-s1"><span class="pl-c1">$</span>guesses</span>);
    }</pre></div>
<p dir="auto">the condition <code class="notranslate">if ($classMetadata-&gt;hasMemberMetadatas($property))</code> skips the entire guess because of this. this condition does not seem to be neccessary since in the next line:<br>
<code class="notranslate">$memberMetadatas = $classMetadata-&gt;getMemberMetadatas($property);</code></p>
<p dir="auto"><code class="notranslate">getMemberMetadatas</code> has a guard for missing metadata anyway and returns an empty array in that case which then skips through the foreach and does a false valueguess for required as it should.<br>
If I remove this condition everything works fine. the guess actually makes it through and works properly. I could not find a good reason why this condition is here but someone more familiar with the code might know better.</p>
<p dir="auto">Regards</p>
<p dir="auto">A mistake that I frequently see is that people forget to call <code class="notranslate">createView()</code> when passing a form to the view. What do you think about converting <code class="notranslate">FormInterface</code> instances to <code class="notranslate">FormView</code> objects automatically by calling <code class="notranslate">createView()</code>, for example, in a listener?</p>