Description
Symfony version(s) affected: 3.4+
Description
In Symfony 3.4 validation groups were added to the Valid() constraint as an option. Yet validation groups for Valid() behave completely different than with any other assertion: usually validation groups define if an assertion should trigger or not. With Valid() it also redefines the validation group for the object and replaces the validation group (which is also not in the documentation) leading to reduced usability and a high chance of things working completely different than anticipated.
How to reproduce
use Symfony\Component\Validator\Constraints as Assert;
class Address
{
/**
* @Assert\NotNull()
* @Assert\NotBlank()
*/
public $street;
/**
* @Assert\NotNull(groups={"cityAddress"})
*/
public $city;
}
class Order
{
/**
* @Assert\NotNull
*/
public $firstName = '';
/**
* @Assert\Valid()
*/
public $address;
/**
* @Assert\Valid(groups={"alternateInvoiceAddress"})
*/
public $invoiceAddress;
}
$order = new Order();
$order->address = new Address();
$order->invoiceAddress = new Address();
// Leads to two violations: address.street should not be null
// and address.street should not be blank - so far so good
$violations = $validator->validate($order);
// Leads to the same two violations, as "alternateInvoiceAddress"
// triggers validation for $invoiceAddress, but only for the
// "alternateInvoiceAddress" validation group within that object,
// and "cityAddress" is never propagated to the $invoiceAddress object
$violations = $validator->validate($order, null, ['Default', 'alternateInvoiceAddress', 'cityAddress']);
(I can give a fully working PHP example file if necessary, the above is the most relevant part of it)
Possible Solution
A defined validation group on an assertion should only define when that specific assertion is triggered - in the case of Valid() the assertion means "validate the object". The current double meaning for Valid() is "only trigger when this validation group is set AND only validate this validation group in the subclass" which limits the usefulness severely (GroupSequence
cannot be used in the subclass, reusable subclasses are very hard to do) and the behavior is very unintuitive.
Defining which validation groups should be checked in a subclass could be a separate feature, then it would be useful, and by default all validation groups from the parent should be passed to the child - this already happens when no validation group is specified for Valid().
I also think this is a limitation which will come up often in real world examples, like in the example above (which is very near my actual use case): specifying a shipping address and an alternate but optional invoice address, which is only validated if a checkbox is selected - already this simple example is impossible to solve as it stands now. At the same time I cannot see many real world applications for the current behavior.