Skip to content

[Validator] add groups support to the Valid constraint #21111

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

Merged
merged 1 commit into from
Aug 5, 2017

Conversation

xabbuh
Copy link
Member

@xabbuh xabbuh commented Dec 31, 2016

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

throw new UnexpectedTypeException($constraint, __NAMESPACE__.'\Valid');
}

$violations = $this->context->getValidator()->validate($value, null, array($this->context->getGroup()));
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not completely sure if handling the traversal really should be done here or if we should better add some special handling for the Valid constraint to the RecursiveContextualValidator instead. Any opinions are warmly welcome.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks clean to me like this.

@kgilden
Copy link
Contributor

kgilden commented Dec 31, 2016

@xabbuh awesome! I confirmed it works as would validation groups work for every other constraint, i.e. in validation.xml you add groups to Valid and now pass the validation_groups option when creating the form. Valid will only be applied if any of the groups in validation_groups equals those specified in validation.xml.

However, I do think there is one super useful use case not covered. Consider a Profile object having profilePicture and coverPhoto properties - both of type Image, which internally has a file property of type File. Let's say we have different maximum file sizes for both of them, so we add 2 File constraints to the Image object. And both properties should be valid at all times for Profile.

<class name="Profile">
  <property name="profilePicture">
    <constraint name="Valid" />
  </property>
  <property name="coverPhoto">
    <constraint name="Valid" />
  </property>
</class>

<class name="Image">
  <property name="file">
    <constraint name="File">
      <option name="maxSize">100k</option>
      <option name="groups">
        <value>profile-picture</value>
      </option>
    </constraint>
    <constraint name="File">
      <option name="maxSize">1000k</option>
      <option name="groups">
        <value>cover-photo</value>
      </option>
    </constraint>
  </property>
</class>

In ProfileType we'd have something like this probably.

$builder->add('profilePicture', ImageType::class, ['validation_groups' => ['profile-picture']]);
$builder->add('coverPhoto', ImageType::class, ['validation_groups' => ['cover-photo']);

But try as you might, it seems to me that validation for these File objects does not kick in.

$form = $factory->create(ProfileType::class);
$form->handleRequest($request);
$form->isValid(); // returns true for files larger than what was specified

Of course this is a bit nonsensical example, but I tried to simplify as much as possible. In one of my projects we for example have Document objects which wrap all files and in one of the entities one property can contain only XML files and the other one can accept a different whitelist of types.

@xabbuh
Copy link
Member Author

xabbuh commented Dec 31, 2016

@kgilden This seems logical to me as the root form itself is validated in the Default group. Configuration validation_groups for the root form to be, for example, ['Default', 'profile-picture', 'cover-photo'] should do the trick.

@kgilden
Copy link
Contributor

kgilden commented Dec 31, 2016

@xabbuh ahh yes, sorry for the confusion. I should note that this patch is still required to make my example above work. The same groups must also be added to Valid constraints and then it works as expected.

@xabbuh
Copy link
Member Author

xabbuh commented Dec 31, 2016

@kgilden You are right. But IMO this behaviour is straight-forward with how validation groups in Symfony work in general. Do you disagree with that?

@xabbuh xabbuh added this to the 3.x milestone Dec 31, 2016
@kgilden
Copy link
Contributor

kgilden commented Dec 31, 2016

@xabbuh it's straight-forward if for example the File constraint is applied to two distinct properties on the root object, i.e.

<class name="Profile">
  <property name="profilePicture">
    <constraint name="File">
        <!-- distinct options -->
    </constraint>
  </property>
  <property name="coverPhoto">
    <constraint name="File">
        <!-- distinct options -->
    </constraint>
  </property>
</class>

What I think is currently not possible is when you want to validate the same objects deeper. After this PR it's possible to do as follows.

<class name="Profile">
  <property name="profilePicture">
    <constraint name="Valid">
      <option name="groups">
        <value>profile-picture</value>
      </option>
    </constraint>
  </property>
  <property name="coverPhoto">
    <constraint name="Valid">
      <option name="groups">
        <value>cover-photo</value>
      </option>
    </constraint>
  </property>
</class>

<class name="Image">
  <property name="file">
    <constraint name="File">
      <option name="maxSize">100k</option>
      <option name="groups">
        <value>profile-picture</value>
      </option>
    </constraint>
    <constraint name="File">
      <option name="maxSize">1000k</option>
      <option name="groups">
        <value>cover-photo</value>
      </option>
    </constraint>
  </property>
</class>
class ProfileType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder->add('profilePicture', ImageType::class);
        $builder->add('coverPhoto', ImageType::class);
    }

    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults(['validation_groups' => ['Default', 'cover-photo', 'profile-picture']]);
    }
}

Correct me if I'm wrong, but I don't think there was a way to do it prior to this PR.

@xabbuh
Copy link
Member Author

xabbuh commented Dec 31, 2016

Indeed, that's not possible right now.

@pourquoi
Copy link

+1 for this feature. The use case described by kgilden happens regularly for me (deep cascade validation groups)

@fabpot
Copy link
Member

fabpot commented Mar 1, 2017

@xabbuh Can you add a note in the CHANGELOG before I can merge?

@xabbuh
Copy link
Member Author

xabbuh commented Mar 1, 2017

@fabpot done, but are we sure that this is the right way to implement it?

@xabbuh xabbuh changed the base branch from master to 3.4 May 18, 2017 07:16
@xabbuh
Copy link
Member Author

xabbuh commented Jul 12, 2017

ping @symfony/deciders

throw new UnexpectedTypeException($constraint, __NAMESPACE__.'\Valid');
}

$violations = $this->context->getValidator()->validate($value, null, array($this->context->getGroup()));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks clean to me like this.

@fabpot
Copy link
Member

fabpot commented Aug 5, 2017

Thank you @xabbuh.

@fabpot fabpot merged commit 0ca27cc into symfony:3.4 Aug 5, 2017
fabpot added a commit that referenced this pull request 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
@xabbuh xabbuh deleted the issue-3622 branch August 5, 2017 17:42
This was referenced Oct 18, 2017
@Atiragram
Copy link

Hi,
What if value is null? i need @Assert\Valid only after and when @Assert\NotNull is valid, currently i get 500 error because of that empty value

@ogizanagi
Copy link
Contributor

@Atiragram : This is already supposed to be fixed in #25297, available since 3.4.1. If you still encounter an issue, please open a new issue with a reproducer or a failing test case. Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

9 participants