Новое в Symfony 5.1: Повторно используемые наборы ограничений

В некоторых приложениях часто используется один и тот же набор ограничений в нескольких местах. Рассмотрим, например, приложение, которое позволяет регистрировать пользователей, изменять пароли, запоминать забытые пароли и т. д. Это приложение может использовать разные DTO для каждой функции, но все они содержат новый пароль пользователя, который должен проверяться одинаково во всех случаях.

В Symfony 5.1 вы можете быстро создать валидатор, использующий другие ограничения, независимо от того, встроены они или сделаны на заказ, благодаря новому ограничению Compound:

namespace App\Validator;

use Symfony\Component\Validator\Constraints\Compound;
use Symfony\Component\Validator\Constraints\Length;
use Symfony\Component\Validator\Constraints\NotBlank;
use Symfony\Component\Validator\Constraints\NotCompromisedPassword;
use Symfony\Component\Validator\Constraints\Type;

/**
 * @Annotation
 */
class MatchesPasswordRequirements extends Compound
{
    protected function getConstraints(array $options): array
    {
        return [
            new NotBlank(),
            new Type('string'),
            new Length(['min' => 12]),
            new NotCompromisedPassword(),
        ];
    }
}

Теперь вы можете применить это ограничение к вашим объектам как обычно:

namespace App\Dto;

// ...
use App\Validator\MatchesPasswordRequirements;

class ChangePasswordDto
{
    /**
     * @MatchesPasswordRequirements
     */
    private $newPassword;

    // ...
}

Проверять ограничения последовательно

В Symfony 5.1 также добавили другую, но связанную, функцию для последовательной проверки набора ограничений. Это было уже возможно благодаря ограничению GroupSequence, но в Symfony 5.1 упростили эту функцию с введением нового ограничения Sequential.

Аргумент ограничения Sequentially представляет собой набор из одного или нескольких ограничений. Symfony будет применять их в том же порядке, и в случае сбоя любого из них он остановится, а остальные ограничения не будут проверены. Это полезно для предотвращения как непредвиденных исключений типов, вызванных некоторыми ограничениями, так и ненужных вызовов медленных ограничений:

/**
 * @var string
 *
 * @Assert\Sequentially({
 *     @Assert\Type("string"),
 *     @Assert\Length(min="4"),
 *     @Assert\Regex("[a-z]"),
 *     @SomeCustomConstraintWithHeavyExternalCalls(),
 * })
 */
public $someProperty;