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

Дата обновления перевода 2023-01-09

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

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

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

  • Attributes
  • YAML
  • XML
  • PHP
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// src/Entity/User.php
namespace App\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: 'Пароль не может совпадать с вашим именем пользователя',
        groups: ['Strict'],
    )]
    public function isPasswordSafe()
    {
        return ($this->username !== $this->password);
    }
}

В этом примере, вначале будут валидированы все ограничения в группе User (что то же самое, что и группа Default). Только если все ограничения в этой группе будут валидны, будут валидирована вторая группа - Strict.

Caution

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

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

Caution

Вызов validate() с группой в последовательности (Strict - в предыдущем примере) приведет к валидации только с этой группой, а не со всеми группами в последовательности. Это так, потому что последовательность теперь ссылается на групповую валидацию Default.

Вы также можете определить последовательность группы в опции формы validation_groups:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// src/Form/MyType.php
namespace App\Form;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Validator\Constraints\GroupSequence;
// ...

class MyType extends AbstractType
{
    // ...
    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults([
            'validation_groups' => new GroupSequence(['First', 'Second']),
        ]);
    }
}

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

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

  • Attributes
  • YAML
  • XML
  • PHP
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// src/Entity/User.php
namespace App\Entity;

use Symfony\Component\Validator\Constraints as Assert;

class User
{
    #[Assert\NotBlank]
    private $name;

    #[Assert\CardScheme(
        schemes: [Assert\CardScheme::VISA],
        groups: ['Premium'],
    )]
    private $creditCard;

    // ...
}

Теперь, измените класс User, чтобы реализовать GroupSequenceProviderInterface и добавьте метод getGroupSequence(), который должен вернуть массив групп для использования:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// src/Entity/User.php
namespace App\Entity;

// ...
use Symfony\Component\Validator\GroupSequenceProviderInterface;

class User implements GroupSequenceProviderInterface
{
    // ...

    public function getGroupSequence(): array|GroupSequence
    {
        // при возвращении простого массива, если в любой из групп есть нарушение,
        // остальные группы не валидируются. Например, если 'User' неудачна, то
        // 'Premium' и 'Api' не валидируются:
        return ['User', 'Premium', 'Api'];

        // при возвращении встроенного масива, все группы, включенные в каждый массив,
        // валидируются. Например, если 'User' неудачна, 'Premium' также валидируется
        // (и вы получите и ее нарушения), но 'Api' не будет валидирована:
        return [['User', 'Premium'], 'Api'];
    }
}

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

  • Attributes
  • YAML
  • XML
  • PHP
1
2
3
4
5
6
7
8
9
10
// src/Entity/User.php
namespace App\Entity;

// ...

#[Assert\GroupSequenceProvider]
class User implements GroupSequenceProviderInterface
{
    // ...
}

Как последовательно применять ограничения в одном свойстве

Иногда вам может захотеться применять ограничения последовательно в одном свойстве. Оганичение Sequentially может решить это за вас более прямым путем, чем использование GroupSequence.