Валидация

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

Валидация

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

Symfony содержит компонент Validator, который упрощает эту задачу. Этот компонент основан на документе спецификация валидации JSR303 Bean.

Установка

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

1
$ composer require symfony/validator

Note

Если ваше приложение не использует Symfony Flex, вам может понадобиться проделать некоторую конфигурацию вручную, чтобы подключить валидацию. Посмотрите справочник конфигурации Валидации .

Основы валидации

Самый лучший способ понять валидацию - это увидеть её в действии. Для начала, предположим, что вы создали обычный PHP-объект, который вам нужно использовать в вашем приложении:

1
2
3
4
5
6
7
// src/Entity/Author.php
namespace App\Entity;

class Author
{
    private $name;
}

Пока это обычный класс, который служит какой-то цели внутри вашего приложения. Задача валидации заключается в том, чтобы сообщить вам - являются ли данные объекта корректными (валидными). Для этого, вам нужно настроить перечень правил (называемых ограничениями (constraints)), которым объект должен соответствовать, чтобы быть валидным. Эти правила обычно определяются с использованием PHP-кода, но также могут быть определены, как файлы .yaml или .xml внутри каталога config/validator/:

Например, для того, чтобы гарантировать, что свойство $name не пустое, добавьте следующий код:

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

// ...
use Symfony\Component\Validator\Constraints as Assert;

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

Добавление этой конфигурации самой по себе еще не гарантирует, что значение не будет пустым; вы все еще можете установить его как пустое значение, если вы этого хотите. Чтобы действительно гарантировать, что значение примкнет к ограничнению, объект должен быть переда сервису влаидатора на проверку.

Tip

Валидатор Symfony использует рефлексию PHP, а также методы "геттера", чтобы получить значение любого свойства; они могут быть публичными, частными или защищенными (см. ).

Использование сервиса валидатор

Далее, чтобы проверить объект Author, используйте метод validate() сервиса validator (который реализует ValidatorInterface). Обязанности у validator простые: прочитать ограничения (т.е. правила) для класса, и определить, соответствуют ли данные из объекта этим ограничениям. Если валидация проходит с ошибкой, возвращается список ошибок (класс ConstraintViolationList). Давайте рассмотрим этот простой пример изнутри контроллера:

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
// ...
use App\Entity\Author;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Validator\Validator\ValidatorInterface;

// ...
public function author(ValidatorInterface $validator)
{
    $author = new Author();

    // ... сделать что-то с объектом $author

    $errors = $validator->validate($author);

    if (count($errors) > 0) {
        /*
         * Использует метод __toString в переменной $errors, которая является объектом
         * ConstraintViolationList. Это дает хорошую строку для отладки.
         */
        $errorsString = (string) $errors;

        return new Response($errorsString);
    }

    return new Response('The author is valid! Yes!');
}

Если свойство $name пустое, вы увидите следующее сообщение об ошибке:

1
2
Object(App\Entity\Author).name:
    Это значение не должно быть пустым.

Если же вы укажете значение для свойства name, появится сообщение об успешной валидации.

Tip

В большинстве случаев, вы не будете напрямую взаимодействовать с сервисом validator и вам не нужно будет беспокоиться об отображении ошибок. Зачастую вы будете использовать валидацию косвенно при обработке данных из отправленных приложению форм. Подробнее об этом смотрите тут: как валидировать формы Symfony .

Вы также можете передать коллекцию ошибок в шаблон:

1
2
3
4
5
if (count($errors) > 0) {
    return $this->render('author/validation.html.twig', [
        'errors' => $errors,
    ]);
}

Внутри шаблона вы можете вывести список ошибок так, как вам нужно:

1
2
3
4
5
6
7
{# templates/author/validation.html.twig #}
<h3>Автор имеет следующие ошибки</h3>
<ul>
{% for error in errors %}
    <li>{{ error.message }}</li>
{% endfor %}
</ul>

Note

Каждая ошибка валидации (называемая «нарушение ограничения»), представлена объектом ConstraintViolation.

Вызываемые валидации

Validation также позволяет вам создавать замыкание, чтобы валидировать значения в соотношении с набором ограничений (полезно, к примеру, при валидации ответов команд Console или при валидации значений OptionsResolver):

createCallable()
Возвращает замыкание, которое вызывает ValidationFailedException когда ограничения не совпадают.
createIsValidCallable()
Возвращает замыкание, которое возвращает false когда ограничения не совпадают.
.. index::
single: Валидация; Ограничения

Ограничения

Validator создан для того, чтобы проверять объекты на соответствие ограничениям (т.е. правилам). Для того чтобы валидировать объект, просто укажите для его класса одно или несколько ограничений, и передайте его сервису валидации (validator).

За кулисами, ограничение - это просто PHP-объект, который выполняет утвердительное выражение. В настоящей жизни, ограничение может выглядеть так: "пирог не должен подгореть". В Symfony ограничения выглядят похожим образом: это утверждения, что некоторое условие истинно. Учитывая значение, ограничение скажет вам, соответствует ли это значение правилам ограничения.

Поддерживаемые ограничения

Symfony содержит большое количество самых необходимых ограничений:

Основные ограничения

Вот основные ограничения: используйте их, чтобы утверждать очень базовые вещи о значении свойств или возвратном значении методов вашего объекта.

Другие ограничения

Вы также можете создавать свои собственные ограничения. Эта тема раскрыта в статье Как создать пользовательское ограничение валидации.

Конфигурация ограничений

Некоторые ограничения, как например NotBlank просты, в то время как другие, например Choice - имеют несколько опций конфигурации. Предположим, что класс Author имеет свойство genre (жанр), которое определяет жанр литературы, который в основном ассоциируется с автором, и которое можно установить в значение "беллетристика" или "не беллетристика" ("fiction" или "non-fiction"):

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

// ...
use Symfony\Component\Validator\Constraints as Assert;

class Author
{
    #[Assert\Choice(
        choices: ['fiction', 'non-fiction'],
        message: 'Choose a valid genre.',
    )]
    private $genre;

    // ...
}

Опции ограничения всегда могут быть переданы в виде массива. Однако, некоторые ограничения также позволяют вам передать значение одной опции «по умолчанию» вместо массива. В случае с ограничением Choice, опции choices можно указать таким образом.

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

// ...
use Symfony\Component\Validator\Constraints as Assert;

class Author
{
    #[Assert\Choice(['fiction', 'non-fiction'])]
    private $genre;

    // ...
}

Такая возможность позволяет сделать настройку самых распространенных опций ограничения короче и быстрее.

Если вы не уверены, как нужно указывать опцию, сверьтесь с пространством имен Symfony\Component\Validator\Constraints касательно ограничения или же поступайте безопасно - всегда передавая массив опций (как показано в первом методе выше).

Ограничения в классах формы

Ограничения могут быть обозначены во время построения форм с помощью опции constraints полей формы:

1
2
3
4
5
6
7
8
9
public function buildForm(FormBuilderInterface $builder, array $options)
{
    $builder
        ->add('myField', TextType::class, [
            'required' => true,
            'constraints' => [new Length(['min' => 3])],
        ])
    ;
}

Цели ограничения

Ограничения могут быть применены к свойству класса (например, name), публичному геттер-методу (например, getFullName()) или целому классу. Первый вариант наиболее распространенный и легкий. Однако ограничения геттера позволяют вам указывать более сложные правила валидации. И, наконец, ограничения класса предназначены для случаев, когда вы хотите валидировать класс в качестве единого целого.

Свойства

Валидация свойств класса - самая простая техника валидации. Symfony позволяет вам проверять приватные, защищенные или публичные свойства. Ниже вы увидите, как сделать так, чтобы свойство $firstName класса Author имело как минимум 3 символа.

  • Attributes
  • YAML
  • XML
  • PHP
1
2
3
4
5
6
7
8
9
10
11
// src/Entity/Author.php

// ...
use Symfony\Component\Validator\Constraints as Assert;

class Author
{
    #[Assert\NotBlank]
    #[Assert\Length(min: 3)]
    private $firstName;
}

Caution

Валидатор будет использовать значение null если инициализация типизированного свойства была отменена. Это может вызвать неожиданое поведение, если свойство содержит значение при инициализации. Чтобы избежать этого, убедитесь, что все свойства инициалищированы до их валидации.

Геттеры

Ограничения также могут применяться для того, чтобы вернуть значение метода. Symfony позволяет вам добавлять ограничение к любому публичному методу, имя которого начинается с «get», «is» или «has». В этой книге, подобные методы называются общим словом «геттеры».

Преимуществом этой техники является то, что она позволяет вам валидировать ваш объект динамично. Например, представьте, что вам нужно убедиться, что поле пароля не совпадает с именем пользователя (из соображений безопасности). Вы можете сделать это создав метод isPasswordLegal() и указав, что этот метод должен вернуться как true:

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

// ...
use Symfony\Component\Validator\Constraints as Assert;

class Author
{
    #[Assert\IsTrue(message: 'The password cannot match your first name')]
    public function isPasswordSafe()
    {
        // ... return true or false
    }
}

Теперь создайте метод isPasswordLegal() и добавьте его в нужную вам логику:

1
2
3
4
public function isPasswordLegal()
{
    return $this->firstName !== $this->password;
}

Note

Самые внимательные из вас заметили, что префикс геттера («get», «is» или «has») опущен при отображении. Это позволит вам применить ограничение к свойству с таким же именем позже ( или наоборот), не изменяя логики валидации.

Классы

Некоторые ограничения применяются к целому валидируемому классу. Например ограничение Callback (обратный вызов) - это универсальное ограничение, которое применяется к самому классу. Когда этот класс валидируется, методы, указанные ограничением, просто выполняются, что позволяет проводить более избирательную валидацию.

Отладка ограничений

Используйте команду debug:validator, чтобы перечислить ограничения валидации данного класса:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
$ php bin/console debug:validator 'App\Entity\SomeClass'

    App\Entity\SomeClass
    -----------------------------------------------------

    +---------------+--------------------------------------------------+-----------+------------------------------------------------------------+
    | Свойство      | Имя                                              | Группы    | Опции                                                      |
    +---------------+--------------------------------------------------+-----------+------------------------------------------------------------+
    | firstArgument | Symfony\Component\Validator\Constraints\NotBlank | по        | [                                                          |
    |               |                                                  | умолчанию |   "message" => "This value should not be blank.",          |
    |               |                                                  |           |   "allowNull" => false,                                    |
    |               |                                                  |           |   "normalizer" => null,                                    |
    |               |                                                  |           |   "payload" => null                                        |
    |               |                                                  |           | ]                                                          |
    | firstArgument | Symfony\Component\Validator\Constraints\Email    | по        | [                                                          |
    |               |                                                  | умолчанию |   "message" => "This value is not a valid email address.", |
    |               |                                                  |           |   "mode" => null,                                          |
    |               |                                                  |           |   "normalizer" => null,                                    |
    |               |                                                  |           |   "payload" => null                                        |
    |               |                                                  |           | ]                                                          |
    +---------------+--------------------------------------------------+---------+------------------------------------------------------------+

Вы также можете валидировать все классы, хранящиеся в данном каталоге:

1
$ php bin/console debug:validator src/Entity

Заключение

Validator Symfony - это мощный инструмент, который используется для получения гарантий, что данные некоторого объекта "валидные". Сила валидации - в ограничениях, которые являются правилами, которые вы можете применить к свойствам или геттер-методам вашего объекта. И несмотря на то, что вы большей частью будете использовать фреймворк валидации косвенно при использовании форм, помните, что он может быть использован где угодно для валидации любого объекта.