Skip to content

[2.2] [Validator] Constraints\Valid does not respect "groups" option #3622

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

Closed
ruimarinho opened this issue Mar 16, 2012 · 37 comments
Closed

Comments

@ruimarinho
Copy link

I have an entity where I'm using the following validators:

/**
 * @Assert\Callback(methods={"isValid"}, groups={"group1", "group2"})
 */
class UserProduct
{
    /**
     * @var string $title
     * @Assert\NotBlank(message="title.blank", groups={"group1", "group2"});
     */
    private $title;

    /**
     * @var Project\AuthenticationBundle\Entity\User
     * @Assert\Valid(groups={"group2"});
     */
    private $user;

Calling $this->get('validator')->validate($userProduct, array('group1')) asserts the class constraint (@Assert\Callback) and the $title property but also the $user one too, even though it's not included in the validation group. The same happens if no groups are passed to the Valid constraint.

Removing the @Assert\Valid() constraint and running the same validation again for group1 does not assert the $user property, as expected.

The MemberMetadata class appears to always force the cascading of the Valid constraint, although I'm unsure if this is exactly related to the issue I'm experiencing.

// Symfony\Component\Validator\Mapping\MemberMetadata.php
        if ($constraint instanceof Valid) {
            $this->cascaded = true;
            $this->collectionCascaded = $constraint->traverse;
        } else {
            parent::addConstraint($constraint);
        }

Running on Symfony2 master.

@stof
Copy link
Member

stof commented Apr 3, 2012

@bschussek ping

@PorridgeBear
Copy link

+1, Assert\Valid does not respect groups. My use-case requires administrators be able to edit Users. We are in beta and a User must use an InviteCode to register. I use Assert\Valid on User.InviteCode for the front-end User registration form. However my admin edit User form does not work because I cannot limit the Assert\Valid to the front-end validation group.

@alterphp
Copy link

alterphp commented May 1, 2012

I got the same problem with a form I use as standalone or as an embedded subform I'd like to valid only with some conditions...

@PorridgeBear
Copy link

Valid seems to be a special case Constraint, though not sure why. It does not have a counterpart ValidValidator, and the MemberMetadata class has a special check for instanceof Valid and in that event does not call addConstraint. This must be by design, but I'd like to hear about a way to specify a traversal of a reference by group or not.

I was able to solve my own use case a little differently. I just added the relevant group to all the validators inside the InviteCode class and then changed my createForm to specify that additional validation group for my sign up, and I leave it out for my admin form which uses Default. Since InviteCode has no default validators it is effectively ignored.

I also wondered whether it could be done using a Callback assertion on the User class and then using the graph walker of the execution context passed to the custom method to either walk or not walk but in the end I didn't have to dig that far.

Clarification on why Valid has no groups param. would be useful though.

@ianfp
Copy link

ianfp commented May 15, 2012

+1. I have the same problem; having Assert\Valid support the groups options would be useful.

@webmozart
Copy link
Contributor

The Symfony2 Validator is (more or less) an implementation of the JSR 303. JSR 303 specifies @Valid to explicitely propagate the validation group to the associated method (i.e. is not restricted by a group in any way). If we'd change this behaviour, @Valid would suddenly only be executed when you validate an object in group "Default" (as that would also be the default group of @Valid. This would break its behaviour in many circumstances.

I'm aware of the problem, but I don't think that making @Valid group-aware is the solution.

@PorridgeBear
Copy link

Looks like the Spring guys have a similar request:

https://jira.springsource.org/browse/SPR-7062
https://jira.springsource.org/browse/SWF-1453

@PorridgeBear
Copy link

Here's how Spring do it ... Valid + validation groups - a new annotation called Validated

http://static.springsource.org/spring/docs/3.1.x/javadoc-api/org/springframework/validation/annotation/Validated.html

They also have a Hint system that can be passed for a SmartValidator

http://static.springsource.org/spring/docs/3.1.x/javadoc-api/org/springframework/validation/SmartValidator.html

@ruimarinho
Copy link
Author

@bschussek after reading your comment, I agree that making @Valid group-aware at this point is not the best solution.

@PorridgeBear findings are inline with what I was going to suggest - a new annotation that would help us specifically tailor this case. Otherwise, is there any workaround or pattern to keep @Valid as it is today and still be able to do what was originally intended with this use case?

@webmozart
Copy link
Contributor

Marking this as feature request, I'll look at this for 2.2.

@shieldo
Copy link
Contributor

shieldo commented Apr 29, 2013

@bschussek: Is there a clear pattern for dealing with the issue that there is here, where you might want to check that a more generic data type in your application (an address, say) is valid within some parent data but don't want to add validation groups to that data type that are outside of its domain (sometimes a data type is either valid or it isn't, regardless of context)? I assume this is what you were getting at when marking this one as a feature request, but I wondered if you'd thought any further about an implementation that doesn't break JSR 303. I'm thinking of something like somehow coercing the Valid constraint to validate using a specific group, rather than the groups that are set for validating the parent data.

@webmozart
Copy link
Contributor

@shieldo Unfortunately I haven't done any more research on this issue so far. :(

@gnutix
Copy link
Contributor

gnutix commented Aug 5, 2013

I've just encountered this issue and I've been able to dodge it by using the cascade_validation => true option on my parent FormType.

@kbond
Copy link
Member

kbond commented Aug 14, 2013

@gnutix awesome, thanks for the work-around!

@trsteel88
Copy link
Contributor

@bschussek Just wondering if this is any closer to becoming a reality? I have quite a few forms that use cascade_validation. I think it would be much cleaner if we could specify groups on the Valid constraint.

@webmozart
Copy link
Contributor

I'm currently trying to figure out whether/how we can solve this issue. A potential solution is to keep the current behavior when the "groups" property of the Valid constraint is null (the default), and to limit it to specific groups when the option is set.

fabpot added a commit that referenced this issue Mar 31, 2014
…mozart)

This PR was merged into the 2.5-dev branch.

Discussion
----------

[WIP][Validator] New NodeTraverser implementation

| Q             | A
| ------------- | ---
| Bug fix?      | yes
| New feature?  | yes
| BC breaks?    | no
| Deprecations? | yes
| Tests pass?   | yes
| Fixed tickets | TODO
| License       | MIT
| Doc PR        | TODO

This PR is ready for review.

#### Todo

- [x] Test extensively to avoid regressions
- [x] Test more extensively
- [x] Finish inline documentation
- [x] Provide a layer to choose the desired API through ValidatorBuilder
- [x] Provide a layer to choose the desired API through FrameworkBundle
- [x] Update UPGRADE file
- [x] Update CHANGELOG
- [ ] Update user documentation

#### Goal

The goal of this PR is to be able to fix the following tickets:

- [x] #6138 Simplify adding of constraint violations
- [x] #7146 Support group sequences in Validator API
- [x] #7432 Poorly implemented Visitor Pattern
- [x] #8617 Control traversal on class level
- [x] #9888 Improve support for collection validation (PR: #9988)

The following tickets are probably fixed, but require testing first:

- [ ] #8376 Using validation_group causes error message to display multiple times
- [ ] #9939 GroupSequences still execute next group if first fail

Of course, full backwards compatibility **must** be guaranteed.

Other tickets I want to fix in follow-up PRs:

* #3622 Constraints\Valid does not respect "groups" option
* #4453 walk constraints by groups
* #7700 Propagate implicit group names in constraints
* #9051 Always ask value event if field isn't in the validating group
* #10163 poor collection validation test coverage
* #10221 TypeValidator does not enforce desired type when value is NULL
* #10495 Class Valid Constraint can't be used on a Form Type

#### In a nutshell

The implementation removes the Visitor pattern, which was implemented badly. I tried fixing it via a NodeTraverser/NodeVisitor implementation, but performance degraded too much so I decided to remove the pattern altogether.

A couple of new features and bug fixes are possible thanks to the new implementation. See below for details.

#### PHP Versions

PHP 5.3.8 and older does not allow to implement two different interfaces which both contain a method with the same name. This is used in the compatibility layer that supports both the old and the new API.

For this reason, the compatibility layer is disabled on PHP < 5.3.9. Older PHP versions need to decide on the old API or the new API (without compatibility layer).

#### Choosing the API Version

The API version can be specified by one of `Validation::API_VERSION_2_4`, `Validation::API_VERSION_2_5` and `Validation::API_VERSION_2_5_BC` to `setApiVersion()` when building the validator:

```php
// Old implementation
$validator = Validation::createValidatorBuilder()
    ->setApiVersion(Validation::API_VERSION_2_4)
    ->getValidator();

// New implementation with BC API
// Does not work on PHP < 5.3.9
$validator = Validation::createValidatorBuilder()
    ->setApiVersion(Validation::API_VERSION_2_5)
    ->getValidator();

// New implementation without BC API
$validator = Validation::createValidatorBuilder()
    ->setApiVersion(Validation::API_VERSION_2_5_BC)
    ->getValidator();
```

#### Features

##### Constraint validation as first-class citizen

The new API merges `validateValue()` and `validate()`. The idea is that the validation of values against constraints should be as simple as possible. Object validation is a special case where an object is tested against the `Valid` constraint. A backwards compatibility layer is provided to use both `validate()` and `validateValue()` with the old signature.

```php
// Validate against explicit constraints
$violations = $validator->validate($firstName, array(
    new NotNull(),
    new Length(array('min' => 3)),
));

// Validate against metadata
$violations = $validator->validate($object);

// Same, more expressive notation
$violations = $validator->validate($object, new Valid());

// Validate each entry against its metadata
$violations = $validator->validate($array);
```

##### Aggregate violations

It is now possible to call the methods of the validator multiple times and aggregate the violations to a common violation list. To do so, call `startContext()`, execute the calls and call `getViolations()` in the end to retrieve the violations:

```php
$violations = $validator->startContext()
    ->validate($title, new NotNull())
    ->validate($text, new NotNull())
    ->validate($author->getName(), new NotNull())
    ->getViolations()
```

Most of the time, you will want to specify a property path for each validation. Use the method `atPath()` for that:

```php
$violations = $validator->startContext()
    ->atPath('title')->validate($title, new NotNull())
    ->atPath('text')->validate($text, new NotNull())
    ->atPath('author.name')->validate($author->getName(), new NotNull())
    ->getViolations()
```

##### Control the context of nested validations

In Symfony <= 2.4, you can validate objects or constraints from within a constraint validator like this:

```php
$this->context->validate($object);
$this->context->validateValue($value, new Length(array('min' => 3)));
```

The validations will run and all violations will be added to the current context.

This way, it is impossible though to validate something, inspect the result and then decide what kind of violations to add to the context. This is needed, for example, for the `Some` constraint (proposed in #9888), which should succeed if any of the validated values did *not* generate violations.

For this reason, the new context API features a method `getValidator()`. This method returns the validator instance, you can use it to validate anything in a new context (as the validator always does):

```php
$validator = $this->context->getValidator();
$violations = $validator->validate($object);

if (count($violations)  > 0) {
    $this->context->addViolation('The validation did not pass');
}
```

You can also explicitly start a new context:

```php
$validator = $this->context->getValidator();
$violations = $validator->startContext()
    ->atPath('title')->validate($title, new NotNull())
    ->atPath('text')->validate($text, new NotNull())
    ->getViolations()
```

If you want to execute the validation in the current context, use the `inContext()` method of the validator instead:

```php
// violations are added to $this->context
$validator->inContext($this->context)
    ->atPath('title')->validate($title, new NotNull())
    ->atPath('text')->validate($text, new NotNull())
;
```

With this feature, #9888 (especially the PR for it: #9988) can be finished.

##### Custom group sequences (#7146)

It is now possible to pass `GroupSequence` instances whenever you can pass a group to the validator. For example:

```php
$violations = $validator->validate($object, new Valid(), new GroupSequence('Basic', 'Strict'));
```

Or in the context of the Form component:

```php
$form = $this->createForm(new BlogType(), new Blog(), array(
    'validation_groups' => new GroupSequence('Basic', 'Strict'),
));
```

##### Constraint violation builders (#6138)

The API for adding constraint violations was simplified:

```php
$this->context->addViolation('message', array('param' => 'value'));

// or

$this->context->buildViolation('message')
    ->atPath('property')
    ->setParameter('param', 'value')
    ->setTranslationDomain('validation_strict')
    ->addViolation();
```

##### Control traversal at class level (#8617)

Currently, it is possible whether to traverse a `Traversable` object or not in the `Valid` constraint:

```php
/**
 * @Assert\Valid(traverse=true)
 */
private $tags = new TagList();
```

(actually, `true` is the default)

In this way, the validator will iterate the `TagList` instance and validate each of the contained objects. You can also set "traverse" to `false` to disable iteration.

What if you want to specify, that `TagList` instances should always (or never) be traversed? That's currently not possible.

With this PR, you can do the following:

```php
/**
 * @Assert\Traverse(false)
 */
class TagList implements \IteratorAggregate
{
    // ...
}
```

#### Follow-up features

Features of the follow-up PRs will be described directly there.

#### Backwards compatibility

I implemented a new `AbstractValidatorTest` which tests both the old and the new implementation for compatibility. I still want to extend this test to make sure we don't introduce any regressions.

Almost none of the existing classes were modified (or only slightly). If users depend on the current (now "legacy") implementation, they will have the choice to continue using it until 3.0 (of course, without the new features).

#### Your task

Congrats, you made it till here :) If you have time, please skim over the code and give me feedback on the overall implementation and the class/method names. Again, no feedback on details yet, there are quite a few areas in the code that are still work in progress.

Thanks,
Bernhard

[1] That means that only the nodes from the root of the graph until the currently validated node are held in memory.

Commits
-------

ca6a722 [Validator] Converted `@deprecate` doc comment into regular doc comment
68d8018 [Validator] Documented changes in the UPGRADE files
b1badea [Validator] Fixed failing CsrfFormLoginTest
0bfde4a [Validator] Fixed misnamed method calls in FrameworkExtension
3dc2b4d [Validator] Made "symfony/property-access" an optional dependency
c5629bb [Validator] Added getObject() to ExecutionContextInterface
9b204c9 [FrameworkBundle] Implemented configuration to select the desired Validator API
0946dbe [Validator] Adapted CHANGELOG
1b111d0 [Validator] Fixed typos pointed out by @cordoval
7bc952d [Validator] Improved inline documentation of RecursiveContextualValidator
166d71a [Validator] Removed unused property
90c27bb [Validator] Removed traverser implementation
3183aed [Validator] Improved performance of cache key generation
029a716 [Validator] Moved logic of replaceDefaultGroup() to validateNode()
2f23d97 [Validator] Reduced number of method calls on the execution context
73c9cc5 [Validator] Optimized performance by calling spl_object_hash() only once per object
94ef21e [Validator] Optimized use statements
1622eb3 [Validator] Fixed reference to removed class in ValidatorBuilder
be508e0 [Validator] Merged DefaultGroupReplacingVisitor and ContextUpdateVisitor into NodeValidationVisitor
50bb84d [Validator] Optimized RecursiveContextualValidator
eed29d8 [Validator] Improved performance of *ContextualValidator::validate()
5c479d8 [Validator] Simplified validateNodeForGroup
eeed509 [Validator] Improved phpdoc of RecursiveValidator
274d4e6 [Validator] Changed ValidatorBuilder to always use LegacyExecutionContext
38e26fb [Validator] Decoupled RecursiveContextualValidator from Node
23534ca [Validator] Added a recursive clone of the new implementation for speed comparison
f61d31e [Validator] Fixed grammar
886e05e [Validator] Removed unused use statement
93fdff7 [Validator] The supported API versions can now be passed to the ValidatorBuilder
987313d [Validator] Improved inline documentation of the violation builder
79387a7 [Validator] Improved inline documentation of the metadata classes
01ceeda [Validator] Improved test coverage of the Traverse constraint
9ca61df [Validator] Improved inline documentation of CascadingStrategy and TraversalStrategy
524a953 [Validator] Improved inline documentation of the validators
9986f03 [Validator] Added inline documentation for the PropertyPath utility class
be7f055 [Validator] Visitors may now abort the traversal by returning false from beforeTraversal()
299c2dc [Validator] Improved test coverage and prevented duplicate validation of constraints
186c115 [Validator] Improved test coverage of NonRecursiveNodeTraverser
822fe47 [Validator] Completed inline documentation of the Node classes and the NodeTraverser
dbce5a2 [Validator] Updated outdated doc blocks
8558377 [Validator] Added deprecation notes
e8fa15b [Validator] Fixed the new validator API under PHP < 5.3.9
2936d10 [Validator] Removed unused use statement
6fc6ecd [Validator] Fixed tests under PHP<5.3.9
778ec24 [Validator] Removed helper class Traversal
76d8c9a [Validator] Fixed typos
4161371 [Validator] Removed unused use statements
aeb6822 [Validator] Improved visitor names
08172bf [Validator] Merged validate(), validateObject() and validateObjects() to simplify usage
51197f6 [Validator] Made traversal of Traversables consistent
117b1b9 [Validator] Wrapped collections into CollectionNode instances
94583a9 [Validator] Changed NodeTraverser to traverse nodes iteratively, not recursively
cf1281f [Validator] Added "Visitor" suffix to all node visitors
230f2a7 [Validator] Fixed exception message
e057b19 [Validator] Decoupled ContextRefresher from ExecutionContext
e440690 [Validator] Renamed validateCollection() to validateObjects()
df41974 [Validator] Changed context manager to context factory
26eafa4 [Validator] Removed unused use statements
bc29591 [Validator] Clearly separated classes supporting the API <2.5/2.5+
a3555fb [Validator] Fixed: Objects are not traversed unless they are instances of Traversable
2c65a28 [Validator] Completed test coverage and documentation of the Node classes
9c9e715 [Validator] Completed documentation of GroupManagerInterface
1e81f3b [Validator] Finished test coverage and documentation of ExecutionContextManager
feb3d6f [Validator] Tested the validation in a separate context
718601c [Validator] Changed validateValue() to validate() in the new API
ee1adad [Validator] Implemented handling of arrays and Traversables in LegacyExecutionContext::validate()
09f744b [Validator] Implemented BC traversal of traversables through validate()
297ba4f [Validator] Added a note why scalars are passed to cascadeObject() in NodeTraverser
9b07b0c [Validator] Implemented BC validation of arrays through validate()
405a03b [Validator] Updated deprecation notes in GroupSequence
499b2bb [Validator] Completed test coverage of ExecutionContext
adc1437 [Validator] Fixed failing tests
4ea3ff6 [Validator] Finished inline documentation of ExecutionContext[Interface]
f6b7288 [Validator] Removed unused use statement
8318286 [Validator] Completed GroupSequence implementation
5fbf848 [Validator] Added note about Callback constraint to CHANGELOG
c1b1e03 [Validator] Added TODO reminder
8ae68c9 [Validator] Made tests green (yay!)
680f1ee [Validator] Renamed $params to $parameters
321d5bb [Validator] Throw exception if ObjectInitializer is constructed without visitors
1156bde [Validator] Extracted code for group sequence resolving into GroupSequenceResolver
b1a9477 [Validator] Added ObjectInitializer visitor
7e3a41d [Validator] Moved visitors to NodeVisitor namespace
a40189c [Validator] Decoupled the new classes a bit
a6ed4ca [Validator] Prototype of the traverser implementation
25cdc68 [Validator] Refactored ValidatorTest and ValidationVisitorTest into an abstract validator test class
@webdevilopers
Copy link

👍

1 similar comment
@TomasVotruba
Copy link
Contributor

👍

@ste93cry
Copy link
Contributor

👍 It's too easy for bundles to provide their own constraints in groups that have a name that user can't use anymore if he wants to override them.

@iBasit
Copy link

iBasit commented Nov 9, 2015

@backbone87
Copy link
Contributor

Maybe something like https://gist.github.com/backbone87/4bd02388a0e96815c3a6 provides the desired behavior?

/**
 * @Validate({
 *   "local_group" = "foreign_group",
 *   "local_group2" = {"foreign_group", "foreign_group2"}
 * })
 */
private $foreignObject;

@aromer
Copy link

aromer commented Dec 10, 2015

Is there any progress on this? Previously "cascade_validation" was a work around, but that's now been deprecated and removed in Symfony 3.0. Is there another solution or work around? I need to be able to conditionally require child forms based on choices in the parent form.

@linaori
Copy link
Contributor

linaori commented Jan 28, 2016

I ran into the same situation, a work around can be to assign the constraint via your form type. It seems like only via annotations it's not provided. If I add the constraint when adding the field to the builder, it works fine.

Basically I have this:

CancelData
    - contract_cancel (ContractCancelData)
CancelType
    $builder->add('contract_cancel', ContractCancelType::class);

ContractCancelData
    - cancel_when
    - cancel_at @Assert\NotBlank(message="cancel.at.required", groups={"cancel-at-date"})

ContractCancelType
    $constraint = new AfterMonth();
    $constraint->groups = ['cancel-at-date'];

    $form->add('cancel_at', TextType::class, ['constraints' => [$constraint]]);

In this case when I set the validation groups to ['Default', 'cancel-at-date'], the AfterMonth is validated but the NotBlank is not. When I had the NotBlank on the type directly, it is triggered. Note that this is added via the event: form.post_set_data.

/cc @webmozart any idea why those constraints are validated but not the annotations?

@kminh
Copy link

kminh commented Jan 30, 2016

This issue makes me wonder whether deprecating cascade_validation in 2.7 is reasonable, since replacing cascade_validation with Assert\Valid (which is recommended) will break any embedded form that has validation_groups specifically set.

cascade_validation should be deprecated only when Assert\Valid has support for the groups option IMO.

@trsteel88
Copy link
Contributor

I agree with @kminh.

@MichaelMackus
Copy link

👍 and definitely agree with @kminh

This is compounded by the issue(s) in #5953... makes overriding/modifying validation groups very annoying, especially with cascades. This means if I use a different validation group(s) for the parent, the children must have their own custom validation group(s) (without using cascade_validation of course, which unfortunately sounds like its deprecated as well...)

@webmozart
Copy link
Contributor

@iltar can you provide a project that reproduces your problem?

@linaori
Copy link
Contributor

linaori commented Mar 25, 2016

@webmozart not as is, managed to get around the problem. It seems like the main issue is that I don't (did) know how to do it right. If I run into this again, I will provide a full example.

@jaikdean
Copy link

jaikdean commented Apr 7, 2016

I have created a package with a Validate constraint which should allow the behaviour most of us here are after: https://github.com/fluoresceco/validate-embedded

It's likely far from perfect as I've only built it this afternoon to suit my own needs, so PRs and issues are welcomed.

@backbone87
Copy link
Contributor

@jaikdean thats basically an implementation of #17622 ?

@jaikdean
Copy link

jaikdean commented Apr 8, 2016

@backbone87 Correct, I knew I'd seen the idea somewhere 😊

@ABM-Dan
Copy link

ABM-Dan commented May 4, 2016

@webmozart a simple example would be two different sibling forms that are bound to the same class through different attributes, like $mailingAddress and $billingAddress, Since they would both @Embed the Address model, they share the validations, but on the outside model class, they would have @Valid("BillingAddress") and @Valid("MailingAddress") so they can be validated or not validated individually.

@rvanlaak
Copy link
Contributor

rvanlaak commented Aug 2, 2016

@ABM-Dan in addition I'd assume that AddressType should be usable outside of the scope of your situation too right? We have other pages that should be able to use the type. So ideally, we do not want to change the validation_groups on the entity or type for every scenario.

Next to that, we also want to validate them dynamically using a closure as documented here.

@ABM-Dan
Copy link

ABM-Dan commented Aug 3, 2016

@rvanlaak I'm not entirely sure what you mean by that. My example described a small and pretty common occurrence in which having validation groups for @Valid would be useful.

@rvanlaak
Copy link
Contributor

rvanlaak commented Aug 3, 2016

Yes I agree to your scenario, what I meant to say is that we have an Address without specific validation groups that simply is used to add addresses.

Next to that, we also have a page where we can apply multiple scenarios on that same AddressType, but we want to apply a validation_group to the Address there so it can be combined with other fields related to the scenarios.

@kgilden
Copy link
Contributor

kgilden commented Dec 21, 2016

@webmozart I'm noticing similar behavior in v2.8.6 what @iltar is seeing. A constraint with a validation group added to a form field is applied, but the same constraint (same options) added via configuration does not trigger a validation error.

Sorry, can't provide an example project right now, hopefully later.

@xabbuh
Copy link
Member

xabbuh commented Dec 31, 2016

I opened #21111 which is an attempt to add group support to the Valid constraint. I would be happy if you could try the patch with your applications and leave feedback in the PR comments.

fabpot added a commit that referenced this issue Aug 5, 2017
… (xabbuh)

This PR was merged into the 3.4 branch.

Discussion
----------

[Validator] add groups support to the Valid constraint

| Q             | A
| ------------- | ---
| Branch?       | 3.4
| Bug fix?      | no
| New feature?  | yes
| BC breaks?    | no
| Deprecations? | no
| Tests pass?   | yes
| Fixed tickets | #3622, #17622
| License       | MIT
| Doc PR        | TODO

Commits
-------

0ca27cc add groups support to the Valid constraint
@fabpot fabpot closed this as completed Aug 5, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests