-
-
Notifications
You must be signed in to change notification settings - Fork 9.7k
Description
Hi,
I think I may have found a bug in ValidatorTypeGuesser , either that or this is quite an unexpected behavior.
Validator guesser for required (guessRequired) does not seem to work properly when there is no constraint defined for a given property at all.
The documentation says: "The required option can be guessed based on the validation rules (i.e. is the field NotBlank or NotNull)"
Which seems to be confirmed by the source code comments in ValidatorTypeGuesser.php:
// If we don't find any constraint telling otherwise, we can assume
// that a field is not required (with LOW_CONFIDENCE)
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)
to reproduce:
The form:
class TestFormType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add("name")
->add("comments");
}
public function getName()
{
return "test_form";
}
}
The entity:
class TestEntity
{
private $name;
private $comments;
// ... getters and setters ...
}
The validator:
<class name="FormExperiment\TestBundle\Entity\TestEntity">
<property name="name">
<constraint name="NotBlank" />
</property>
</class>
and in the controller simply:
$form = $this->createForm(new TestFormType(), new TestEntity());
and then just pass $form->createView()
to the template
Expected behavior:
name
has required="required"
, and comments doesn't have it.
What actually happens:
Both name
and comments
have required="required"
If you add any constraint to 'comments', like:
<property name="comments">
<constraint name="Length">
<option name="max">5</option>
</constraint>
</property>
then it will guess the required option properly (in this case required is false since the constraint is not NotNull or NotBlank)
even more bizarre that if I override the default in the form type with:
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'required' => false
));
}
then both will miss required="required"
regardless of the guessing altogether, seems like the default option is stronger than anything else.
I managed to trace the problem and make the guesser work though:
The main problem is that since constraints are not present for comments at all the whole guessing is skipped here (ValidatorTypeGuesser.php):
protected function guess($class, $property, \Closure $closure, $defaultValue = null)
{
$guesses = array();
$classMetadata = $this->metadataFactory->getMetadataFor($class);
if ($classMetadata->hasMemberMetadatas($property)) {
$memberMetadatas = $classMetadata->getMemberMetadatas($property);
foreach ($memberMetadatas as $memberMetadata) {
$constraints = $memberMetadata->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);
}
the condition if ($classMetadata->hasMemberMetadatas($property))
skips the entire guess because of this. this condition does not seem to be neccessary since in the next line:
$memberMetadatas = $classMetadata->getMemberMetadatas($property);
getMemberMetadatas
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.
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.
Regards