如何按顺序应用验证组

3.4 版本
维护中的版本

在某些情况下,需要按照步骤验证你的组。要做到这一点,你可以使用GroupSequence功能。在这种情况下,给一个对象定义一组顺序,也就是确定组验证的顺序。

例如,假设你有一个User类,并希望他验证用户名和密码不能重复,只有在其他验证都通过后在验证(为了避免多个错误消息)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// src/AppBundle/Entity/User.php
namespace AppBundle\Entity;
 
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Validator\Constraints as Assert;
 
/**
 * @Assert\GroupSequence({"User", "Strict"})
 */
class User implements UserInterface
{
    /**
     * @Assert\NotBlank
     */
    private $username;
 
    /**
     * @Assert\NotBlank
     */
    private $password;
 
    /**
     * @Assert\IsTrue(message="The password cannot match your username", groups={"Strict"})
     */
    public function isPasswordLegal()
    {
        return ($this->username !== $this->password);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# src/AppBundle/Resources/config/validation.yml
AppBundle\Entity\User:
    group_sequence:
        - User
        - Strict
    getters:
        passwordLegal:
            - 'IsTrue':
                message: 'The password cannot match your username'
                groups: [Strict]
    properties:
        username:
            - NotBlank: ~
        password:
            - NotBlank: ~
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<!-- src/AppBundle/Resources/config/validation.xml -->
<?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="AppBundle\Entity\User">
        <property name="username">
            <constraint name="NotBlank" />
        </property>
 
        <property name="password">
            <constraint name="NotBlank" />
        </property>
 
        <getter property="passwordLegal">
            <constraint name="IsTrue">
                <option name="message">The password cannot match your username</option>
                <option name="groups">
                    <value>Strict</value>
                </option>
            </constraint>
        </getter>
 
        <group-sequence>
            <value>User</value>
            <value>Strict</value>
        </group-sequence>
    </class>
</constraint-mapping>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// src/AppBundle/Entity/User.php
namespace AppBundle\Entity;
 
use Symfony\Component\Validator\Mapping\ClassMetadata;
use Symfony\Component\Validator\Constraints as Assert;
 
class User
{
    public static function loadValidatorMetadata(ClassMetadata $metadata)
    {
        $metadata->addPropertyConstraint('username', new Assert\NotBlank());
        $metadata->addPropertyConstraint('password', new Assert\NotBlank());
 
        $metadata->addGetterConstraint('passwordLegal', new Assert\IsTrue(array(
            'message' => 'The password cannot match your first name',
            'groups'  => array('Strict'),
        )));
 
        $metadata->setGroupSequence(array('User', 'Strict'));
    }
}

在这个案例中,他会先验证所有验证中的User组(就好像以前的Default组一样)。只有当这组所有约束都通过时,才会进行第二组验证,strict

正如你已经从上节中看到的,default组和类名组(如User组)都是相同的。然而,使用组顺序时,他们不再相同了。Default组将引用组序列,而不是所有不属于任何群体的约束。 这意味着,当你指定一组顺序,你必须要使用 {ClassName}(例如User)组。当你使用Default,你会得到一个无线递归。(default组引用组顺序,其中将包含default,他会引用同组的组顺序,...组)。

约束组顺序提供器(Providers) 

想象一下一个User实体可以是一个普通的用户也可以是一个高级用户。当他是一个高级用户,user实体应该添加一些额外的约束(例如 信用卡详细信息)。去动态的确认哪个组被激活,你需要创建一个验证组顺序提供器。首先,创建实体并给他一个新的约束组Premium:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// src/AppBundle/Entity/User.php
namespace AppBundle\Entity;
 
use Symfony\Component\Validator\Constraints as Assert;
 
class User
{
    /**
     * @Assert\NotBlank()
     */
    private $name;
 
    /**
     * @Assert\CardScheme(
     *     schemes={"VISA"},
     *     groups={"Premium"},
     * )
     */
    private $creditCard;
 
    // ...
}
1
2
3
4
5
6
7
8
9
# src/AppBundle/Resources/config/validation.yml
AppBundle\Entity\User:
    properties:
        name:
            - NotBlank: ~
        creditCard:
            - CardScheme:
                schemes: [VISA]
                groups: [Premium]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<!-- src/AppBundle/Resources/config/validation.xml -->
<?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="AppBundle\Entity\User">
        <property name="name">
            <constraint name="NotBlank" />
        </property>
 
        <property name="creditCard">
            <constraint name="CardScheme">
                <option name="schemes">
                    <value>VISA</value>
                </option>
                <option name="groups">
                    <value>Premium</value>
                </option>
            </constraint>
        </property>
 
        <!-- ... -->
    </class>
</constraint-mapping>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// src/AppBundle/Entity/User.php
namespace AppBundle\Entity;
 
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Component\Validator\Mapping\ClassMetadata;
 
class User
{
    private $name;
    private $creditCard;
 
    // ...
 
    public static function loadValidatorMetadata(ClassMetadata $metadata)
    {
        $metadata->addPropertyConstraint('name', new Assert\NotBlank());
        $metadata->addPropertyConstraint('creditCard', new Assert\CardScheme(
            'schemes' => array('VISA'),
            'groups'  => array('Premium'),
        ));
    }
}

现在,改写User类并实现 GroupSequenceProviderInterface并且添加getGroupSequence()方法,他能以一个数组形式返回我们使用的组。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// src/AppBundle/Entity/User.php
namespace AppBundle\Entity;
 
// ...
use Symfony\Component\Validator\GroupSequenceProviderInterface;
 
class User implements GroupSequenceProviderInterface
{
    // ...
 
    public function getGroupSequence()
    {
        $groups = array('User');
 
        if ($this->isPremium()) {
            $groups[] = 'Premium';
        }
 
        return $groups;
    }
}

最后,你必须告诉你的验证组件,你的User类使用组顺序进行验证:

1
2
3
4
5
6
7
8
9
10
11
12
// src/AppBundle/Entity/User.php
namespace AppBundle\Entity;
 
// ...
 
/**
 * @Assert\GroupSequenceProvider
 */
class User implements GroupSequenceProviderInterface
{
    // ...
}
1
2
3
# src/AppBundle/Resources/config/validation.yml
AppBundle\Entity\User:
    group_sequence_provider: true
1
2
3
4
5
6
7
8
9
10
11
12
<!-- src/AppBundle/Resources/config/validation.xml -->
<?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="AppBundle\Entity\User">
        <group-sequence-provider />
        <!-- ... -->
    </class>
</constraint-mapping>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// src/AppBundle/Entity/User.php
namespace AppBundle\Entity;
 
// ...
use Symfony\Component\Validator\Mapping\ClassMetadata;
 
class User implements GroupSequenceProviderInterface
{
    // ...
 
    public static function loadValidatorMetadata(ClassMetadata $metadata)
    {
        $metadata->setGroupSequenceProvider(true);
        // ...
    }
}

本文,包括例程代码在内,采用的是 Creative Commons BY-SA 3.0 创作共用授权。

登录symfonychina 发表评论或留下问题(我们会尽量回复)