Issue 1:
Symfony version im using 4.1.0

During development of an app i was using form_row() to render each input with its label and errors, but when i tried to customize it a bit(i wanted the error message to be shown in the bottom of the input) i switched to using form_label(),form_widget(),form_errors(). Once i test it i figured the error message has been duplicated(one above the input and one below most likely rendered by form_errors).

(Yes i did cleared the cache.)

SetupWizardType.php (form class)

public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('language', EntityType::class, [
'translation_domain' => 'labels',
'label' => 'label.languages',
'placeholder' => 'label.choose',
'class' => Languages::class
])
->add('country', EntityType::class,[
'translation_domain' => 'labels',
'label' => 'label.countries',
'placeholder' => 'label.choose',
'class' => Countries::class
])
->add('timezone', EntityType::class,[
'translation_domain' => 'labels',
'label' => 'label.timezones',
'placeholder' => 'label.choose',
'class' => Timezones::class
])
->add('currency', EntityType::class,[
'translation_domain' => 'labels',
'label' => 'label.currencies',
'placeholder' => 'label.choose',
'class' => Currencies::class
]);

}

setup_wizard.twig.html

{{ form_start(form, {method: 'POST', action: path('wizard_index')}) }}

{{ form_label(form.language) }}
{{ form_widget(form.language, {attr: {'onchange': 'this.form.submit();'}}) }}
{{ form_errors(form.language) }}

{{ form_label(form.country) }}
{{ form_widget(form.country) }}
{{ form_errors(form.country) }}

{{ form_label(form.timezone) }}
{{ form_widget(form.timezone) }}
{{ form_errors(form.timezone) }}

{{ form_label(form.currency) }}
{{ form_widget(form.currency) }}
{{ form_errors(form.currency) }}
<div class="form-group">
<button class="btn">{% trans from 'buttons' %}button.prev_step{% endtrans %}</button>
<button class="btn">{% trans from 'buttons' %}button.next_step{% endtrans %}</button>
<input class="btn" type="submit" name="save" value="{% trans from 'buttons' %}button.save{% endtrans %}" formnovalidate>
</div>

{{ form_end(form) }}

If i remove every form_errors() , the message will show only once above the input, that's why i supposed it is rendered by form_widget.

Any thoughts on this?

Issue 2:
Symfony version(s) affected: 3.4.11

Hello,

@Assert\Valid seems to work not the same way on a property, and on a getter.

Here's the context :

On a page, depending of a status, i need to apply different validations groups :

Status | Validation Group

electronic | electronic

full_paper | register

half_paper | electronic & register

some validation should always be present, some only for electronic, some only for register, and when in "half_paper" status, every validation should be applied.

I have 1 parent Entity with 2 children Entities, both in OneToMany.

One @Assert\Valid is applied on the property for 1 children, and one @Assert\Valid is applied on a getter for the second children (because I need to distinguish by name some entities of the second children).

Code :

Parent

Mandate.php

/**
* @var Customer[]|Collection
* @Assert\Valid(groups={"electronic"})
* @Assert\Valid(groups={"register"})
* @ORM\OneToMany(targetEntity="Customer", mappedBy="mandate", cascade={"persist", "remove"})
*/
protected $customers;

/**
* @ORM\OneToMany(targetEntity="Property", mappedBy="mandate", cascade={"persist", "remove"})
*/
protected $properties;

/**
* @Assert\Valid(groups={"electronic"})
* @Assert\Valid(groups={"register"})
*/
public function getMainProperty(): ?Property
{
foreach ($this->properties as $property) {
if ($property->isMain()) {
return $property;
}
}

return null;
}

/**
* @Assert\Valid(groups={"electronic"})
* @Assert\Valid(groups={"register"})
*/
public function getSecondaryProperties(): Collection
{
$properties = new ArrayCollection();
foreach ($this->properties as $property) {
if (!$property->isMain()) {
$properties[] = $property;
}
}

return $properties;
}

By the way, I'm forced to duplicate @Assert\Valid for every group, because if i merge groups in one @Assert\Valid, only the first group detected is applied on the childrens.

Children

Customer.php

/**
* @Assert\NotBlank(groups={"electronic", "register"})
* @ORM\Column(type="string", nullable=true)
*/
protected $firstName;

/**
* @Assert\NotBlank(groups={"electronic"})
* @ORM\Column(type="string", nullable=true)
*/
protected $email;

/**
* @Assert\NotBlank(groups={"register"})
* @ORM\Column(type="string", nullable=true)
*/
protected $city;

Children

Property.php

/**
* @Assert\NotBlank(groups={"electronic", "register"})
* @ORM\Column(type="string", nullable=true)
*/
protected $lotNumber;

/**
* @Assert\NotBlank(groups={"electronic"})
* @ORM\Column(type="string", nullable=true)
*/
protected $lotNature;

/**
* @Assert\NotBlank(groups={"register"})
* @ORM\Column(type="string", nullable=true)
*/
protected $programName;

Here is how i validate :

MandateController.php

/**
* @param ValidatorInterface $validator
* @param Mandate $mandate
* @return \Symfony\Component\HttpFoundation\Response
*/
public function summaryAction(ValidatorInterface $validator, Mandate $mandate)
{
...
// $groups can be : ['electronic'], ['register'] or ['electronic', 'register']
$errors = $validator->validate($mandate, null, $groups);
...
}

Conclusion :

Both group register & electronic are applied on every customers, every fields having the "NotBlank" are triggered.

BUT on properties, only 'electronic' is applied on every properties.

It looks like this is very difficult to work with multi-groups in input on validate() method + multi-group on @Assert on entities. The merging flow looks like hard.

My only solution right now is to work with only 1 group that I will call 'half_paper' and I will apply this group on every @Assert where electronic and/or register are present.

Thanks