Как применять группы валидации последовательно

Как применять группы валидации последовательно

В некоторых случаях, вам нужно будет валидировать ваши группы пошагово. Чтобы сделать это, вы можете использовать функцию GroupSequence. В этом случае, объект определяет групповую последовательность, которая определяет порядок, в котором нужно валидировать группы.

Например, представьте, что у вас есть класс User и вы хотите валидировать то, отличются ли имя ползователя и пароль только в случае, если вся остальная валидация пройдёт успешно (чтобы избежать множественных сообщений об ошибке).

  • Annotations
     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);
        }
    }
    
  • YAML
     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: ~
    
  • XML
     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>
    
  • PHP
     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.

Caution

Как вы уже видели в предыдущей части, группа Default и группа, содержащая имя класса (например, User) были идентичны. Однако, при использовании групповой последовательности, они больше не будут идентичными. Группа Default теперь будет ссылаться на групповую последовательность вместо всех ограничений, которые не принадлежат ни одной группе.

Это означает, что вам нужно использовать группу {ClassName} (например, User), при указывании групповой последовательности. При использовании Default, вы получите бесконечную рекурсию (так как группа Default ссылается на групповую последовательность, содержащую группу Default, которая ссылается на ту же групповую последовательность...).

Поставщики групповой последовательности

Представьте сущность User, которая может быть нормальным или премиум пользователем. Если это премиум пользователь, необходимо добавить некоторые дополнительные ограничения к сущности пользователя (например, информацию о кредитной карте). Чтобы динамически определить, какие группы стоит активировать, вы можете создать поставщика групповой последовательности. Для начала, создайте сущеность и новую группу ограничений под названием Premium:

  • Annotations
     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;
    
        // ...
    }
    
  • YAML
    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]
    
  • XML
     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>
    
  • PHP
     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(array(
                '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;
    }
}

Наконец, вам нужно уведомить компонент валидатор (Validator) о том, что ваш класс User предоставляет последовательность групп для валидации:

  • Annotations
     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
    {
        // ...
    }
    
  • YAML
    1
    2
    3
    # src/AppBundle/Resources/config/validation.yml
    AppBundle\Entity\User:
        group_sequence_provider: true
    
  • XML
     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>
    
  • PHP
     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);
            // ...
        }
    }
    

Эта документация является переводом официальной документации Symfony и предоставляется по свободной лицензии CC BY-SA 3.0.