Permalink

3

Dynamic Validation Groups Using a GroupSequenceProvider in Symfony2

I recently had to build a form that had two possible validation groups. These groups were dependant on the data being submitted. Specifically, I had a file upload and a text input. The user could either enter a hash in the text field, which would then be validated using MinLength and other custom validators OR they could select a torrent file to upload. The file would need to be validated for mime type and size and also a custom validator for validating its contents.

There is no obvious way to accomplish this with standard validation_groups as it implies AND with everything in the group.

The solution is to use a GroupSequenceProvider. This is similar to a callback, but rather than performing the validation in the method itself, it is simply determining which validation groups to apply to this validation sequence. This means that, based on the data in the object, you can change the validation group at validation-time.

This is how to use a GroupSequenceProvider:

In your Resources/config/validation.xml file add the group-sequence-provider tag.

<?xml version="1.0" encoding="UTF-8" ?>
<constraint-mapping xmlns="http://symfony.com/schema/dic/constraint-mapping"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/constraint-mapping http://symfony.com/schema/dic/constraint-mapping/constraint-mapping-1.0.xsd">
<class name="SOTB\CoreBundle\Document\Torrent">
<group-sequence-provider/>
<!-- ...other properties and constraints here... -->
</class>
</constraint-mapping>
view raw validation.xml hosted with ❤ by GitHub

Then on the class that you added the tag to, you must implement the GroupSequenceProviderInterface

<?php
namespace SOTB\CoreBundle\Document\Torrent;
use Symfony\Component\Validator\GroupSequenceProviderInterface;
class Torrent implements GroupSequenceProviderInterface
{
/**
* Returns which validation groups should be used for a certain state
* of the object.
*
* @return array An array of validation groups
*/
public function getGroupSequence()
{
$groups = array('always');
if (!empty($this->fileName)) {
array_push($groups, 'upload');
}
if (!empty($this->hash)) {
array_push($groups, 'hash');
}
if (empty($this->fileName) && empty($this->hash)) {
array_push($groups, 'either');
}
return $groups;
}
}
view raw Torrent.php hosted with ❤ by GitHub

In this example I have the validation groups always, upload, hash, either. I then specify these groups in my validation.xml for various checks.

For example, this only enforces a minimum hash length if a file was not uploaded but there’s always a max length.

<?xml version="1.0" encoding="UTF-8" ?>
<constraint-mapping xmlns="http://symfony.com/schema/dic/constraint-mapping"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/constraint-mapping http://symfony.com/schema/dic/constraint-mapping/constraint-mapping-1.0.xsd">
<class name="SOTB\CoreBundle\Document\Torrent">
<group-sequence-provider/>
<property name="hash">
<constraint name="MaxLength">
<option name="limit">1024</option>
<option name="groups">
<value>always</value>
</option>
</constraint>
<constraint name="MinLength">
<option name="limit">32</option>
<option name="groups">
<value>hash</value>
</option>
</constraint>
</property>
</class>
</constraint-mapping>
view raw validation.xml hosted with ❤ by GitHub

The pull request for this feature can be found here https://github.com/symfony/symfony/pull/3199. I don’t believe there is any documentation on it as of yet.

Author: Matt Drollette

I am a software developer in Dallas, TX.

3 Comments

  1. Thanks for your post, this is at the moment the only documentation available for this subject.

    If you are returning more then one group in the getGroupSequence method, validation stops when a invalid group is found. I wasn’t aware of that. So if someone is following this tutorial, be warned. If you’re validating more than one group, your user has to step trough all groups by resubmitting the form until everything is fine. Probably that’s not what you want 😉

Leave a Reply

Required fields are marked *.