New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Validator] Valid() with groups should not replace validation groups #32900
Comments
Can you please create a small example application that allows to reproduce your issue? |
Sure, I did just that: Symfony Issue 32900 with Validator.zip Just run It is kept very simple but still with the "real world" example of an Order with two possible Address objects. In reality, the Address object would probably be a lot more complex (more fields, a |
I added another "test case" to make the confusing behavior a bit more obvious: Symfony Issue 32900 with Validator.zip It shows in more detail how the validation group definition for |
I went through all the relevant source code in the validator component and found the reasons for the current behavior, and a way to make The current behavior is a remnant of the cascading behavior before the
I can try to do a pull request if this would be a viable solution. I think it would be especially important to add more tests for the behavior of If my issue is still not clear I can also go into more detail / point out the exact code parts in the validator component, as I am now very familiar with the current behavior and the code leading to it. |
Thank you for the example. I had a look at the test, but I have to admit that the behaviour looks expected to me. Using |
Rereading your initial description it seems that this is what you tried to explain. I can see that there could be a new for the behaviour that you describe, but that's not how it was intended when group support was added to the |
Just to clarify, @iquito proposes that groups on |
The main problem I see is that the current behavior is not very useful (and it is not documented) - it does not make sense to me in any project or form I work on (and I have thought about how to use it sensibly: I came up with no use case at all), because triggering and "filtering" the validation group are two very different use cases and do not belong together. If you really think BC break is a big concern here (I don't think so in this case) one could just introduce a new annotation which behaves as expected and is consistent with "regular" validation groups. It would be fairly easy to implement (I could make a pull request), one would just need to find a sensible name for it. What @vudaltsov suggests is another option, but it adds additional behavior to an already difficult to understand validation. I would rather have an annotation which behaves exactly as one would expect. |
Creating a new annotation would be BC compatible, and it would make it possible to give people an "upgrade strategy" and to deprecate the old |
In a nutshell, this is the behavior I am proposing (I named the annotation /**
* Validates $someobject, Cascade annotation belongs to "Default" group like
* any other assertion would normally, so if "Default" validation group is
* not validated, Cascade is not triggered, otherwise all validation groups are
* forwarded to $someobject
*
* @Assert\Cascade()
*/
public $someobject;
/**
* Validates $someobject only if the "basic" validation group is triggered,
* forwards all validation groups to $someobject
*
* @Assert\Cascade(groups={"basic"})
*/
public $someobject;
/**
* Validates $someobject only if the "basic" validation group is triggered,
* triggers only the "someobjectgroup" validation group in $someobject
*
* @Assert\Cascade(groups={"basic"},trigger={"someobjectgroup"})
*/
public $someobject; |
@iquito , the problem with the new annotation is that it's new. It takes time for beginners to figure out that recursive validation requires We can keep the current behavior as a default, since it didn't arouse a lot of issues so far. And add an opt-in option to turn on the custom behavior you propose (which seems to me more logical, indeed). Anyway, you can create a PR on https://github.com/symfony/symfony-docs to clarify the current behavior. |
@vudaltsov The problem with the current behavior is that it is not completely fixable - for example |
I created an open source package to partly solve the problem, with a new https://github.com/squirrelphp/validator-cascade (MIT licensed) For my projects this is useable, and I included documentation on how to use it and why it is more straightforward to use than An alternative would be to change |
I have the same problem in my project and it gets worse when a property has 2 constraints with different groups. |
@Laulibrius This sounds different. Can you open a new issue and create a small example application that allows to reproduce? |
@Laulibrius Looks like what you reported could be the same as #36157 which will be fixed by #36216. |
|
Thank you for this suggestion. |
Just a quick reminder to make a comment on this. If I don't hear anything I'll close this. |
Hey, I didn't hear anything so I'm going to close it. Feel free to comment if this is still relevant, I can always reopen! |
This is relevant, the problem is urgent. |
@vlastv I suggest that you urgently implement this feature then and send a PR. |
After bumping into this issue head-on, I tend to agree with @iquito that using groups for passing them deeper as opposed to checking when the current ( I have run into a very specific use case of validating nested DTOs and I can't seem to find a way out of it due to this behaviour. First, I had:
But if in the request the First attempt was to use So then I have resorted to And the other way round, putting Any ideas how could I work around all that with the current feature set or does this actually calls for some changes in the Validator internals? I believe I would not have this problem if the |
Ran into this problem today and was very confused by it. Finally solved it with the solution mentioned in #40741. My use case is pretty simple (or so I thought 🙂 ): I want to validate an embedded form type (with its own data class and constraints) only if a specific field is set to a specific value. Solved it like this with the final class ProfileAddressRequest
{
#[Assert\Type(type: 'bool')]
public mixed $addressVisible;
/**
* This default constraint does not work.
* Form submits without validating the embedded.
* #[Assert\Valid(groups: ['address'])]
*/
#[ValidEmbeddable(groups: ['address'])]
public AddressRequest $address;
public function __construct()
{
$this->addressVisible = null;
$this->address = new AddressRequest();
}
} final class ProfileAddressType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$builder->add(
'addressVisible',
ChoiceType::class,
[
'label' => 'Show address?',
'choices' => [
'Yes' => true,
'No' => false,
],
'expanded' => true,
],
);
$builder->add(
'address',
AddressType::class,
);
}
public function configureOptions(OptionsResolver $resolver): void
{
$resolver->setDefaults(
[
'data_class' => ProfileAddressRequest::class,
'validation_groups' => static function (FormInterface $form) {
if ($form->get('addressVisible')->getData() === false) {
return ['Default'];
}
return ['Default', 'address'];
},
],
);
}
} Maybe this is helpful for somebody running into this. |
This is still a problem in Symfony. I have a simple use case. I have an object Parent which has a collection of Child objects. When I validate the Parent, I sometimes want to validate the Child objects too, sometimes not. The Child objects use the Default validation group. My assumption was that I can do this:
Then call the validator like |
PR welcome to implement this feature |
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
(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.
The text was updated successfully, but these errors were encountered: