Дата оновлення перекладу 2022-06-02

Валідація

Валідація - це дуже розповсюджена задача для веб-додатку. Дані, які вводяться у форми, мають бути валідовані (перевірені). У той же час, дані мають бути валідовані до того, як вони будуть записані в базу даних або ж передані веб-сервісу.

Symfony містить компонент Validator, який спрощує цю задачу. Цей компонент заснований на документі специфікація валідації JSR303 Bean.

Установка

В додатках, які використовують Symfony Flex, виконайте цю команду, щоб встановити валідатор перед його використанням:

1
$ composer require symfony/validator doctrine/annotations

Note

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

Основи валідації

Найкращий спосіб зрозуміти валідацію - це побачити її в дії. Спочатку, припустимо, що ви створили звичайний PHP об’єкт, який вам потрібно використати у вашому додатку:

// src/Entity/Author.php
namespace App\Entity;

class Author
{
    private $name;
}

Поки це звичайний клас, який служить якійсь цілі всередині вашого додатку. Задача валідації полягає в тому, щоб повідомити вам - чи є дані об’єкта коректними (валідними). Для цього вам потрібно налаштувати перелік правил (які називаються обмеження), яким об’єкт має відповідати, щоб бути валідним. Ці правила зазвичай визначаються з використанням PHP-коду, але також можуть бути визначені як файли .yaml або .xml всередині каталогу config/validator/:

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

  • Annotations
     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\NotBlank
         */
        private $name;
    }
    
  • Attributes
     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;
    }
    
  • YAML
    1
    2
    3
    4
    5
    # config/validator/validation.yaml
    App\Entity\Author:
        properties:
            name:
                - NotBlank: ~
    
  • XML
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    <!-- config/validator/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
            https://symfony.com/schema/dic/constraint-mapping/constraint-mapping-1.0.xsd">
    
        <class name="App\Entity\Author">
            <property name="name">
                <constraint name="NotBlank"/>
            </property>
        </class>
    </constraint-mapping>
    
  • PHP
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    // src/Entity/Author.php
    namespace App\Entity;
    // ...
    use Symfony\Component\Validator\Constraints\NotBlank;
    use Symfony\Component\Validator\Mapping\ClassMetadata;
    
    class Author
    {
        private $name;
    
        public static function loadValidatorMetadata(ClassMetadata $metadata)
        {
            $metadata->addPropertyConstraint('name', new NotBlank());
        }
    }
    

Додавання цієї конфігурації саме по собі ще не гарантує, що значення не буде порожнім; ви все ще можете встановити його як порожнє значення, якщо ви цього хочете. Щоб дійсно гарантувати, що значення доєднається до обмеження, об’єкт має бути переданий сервісу валідатора на перевірку.

Tip

Валідатор Symfony використовує рефлексію PHP, а такод методи “геттера”, щоб отримати значення будь-якої властивості; вони можуть бути публичними, приватними або захищеними (див. Constraint Targets).

Використання сервісу валідатор

Далі, щоб перевірити об’єкт Author, використайте метод validate() сервісу validator (який реалізує ValidatorInterface). Обов’язки у validator прості: прочитати обмеження (тобто правила) для класу та визначити, чи відповідають дані з об’єкта цим обмеженням. Якщо валідація проходить з помилкою, повертається список помилок (клас ConstraintViolationList). Давайте розглянемо цей простий приклад зсередини контролера:

// ...
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.

Ви також можете передати колекцію помилок у шаблон:

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 також дозволяє вам створювати замикання, щоб валідувати значення у відповідності до набору обмежень (корисно, наприклад, при валідації відповідей команд Консолі або при валідації значень OptionsResolver):

createCallable()
Повертає замикання, яке викликає ValidationFailedException, коли обмеження не співпадають.
createIsValidCallable()
Викликає замикання, яке повертає false, коли обмеження не співпадають.

New in version 5.1: Validation::createCallable() було представлено в Symfony 5.1.

New in version 5.3: Validation::createIsValidCallable() було представлено в Symfony 5.3.

Обмеження

Validator створений для того, щоб перевіряти об’єкти на відповідність обмеженням (тобто правилам). Для того щоб валідувати об’єкт, просто вкажіть для його класу одне або декілька обмежень та передайте його сервісу валідації (validator).

За лаштунками, обмеження - це просто PHP-об’єкт, який виконує ствердний вираз. У реальному житті, обмеження може виглядати так: "пиріг не має згоріти". У Symfony обмеження виглядають схоже: це твердження, що деяка умова є істиною. Враховуючи значення, обмеження скаже вам, чи відповідає це значення правилам обмеження.

Підтримувані обмеження

Symfony містить велику кількість найнеобхідніших обмежень:

Basic Constraints

These are the basic constraints: use them to assert very basic things about the value of properties or the return value of methods on your object.

Date Constraints

Choice Constraints

File Constraints

Financial and other Number Constraints

Other Constraints

Ви також можете створювати власні обмеження. Ця тема охоплена у статті How to Create a Custom Validation Constraint.

Конфігурація обмежень

Деякі обмеження, як наприклад, NotBlank, прості, у той час як інші, наприклад, Choice, мають декілька опцій конфігурації. Припустимо, що клас Author має властивість genre (жанр), яка визначає жанр літератури, який в основному асоціюється з автором, і яку можна встановити у значення “художня” або “нон-фікшн” (“fiction” або “non-fiction”):

  • Annotations
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    // src/Entity/Author.php
    namespace App\Entity;
    
    // ...
    use Symfony\Component\Validator\Constraints as Assert;
    
    class Author
    {
        /**
         * @Assert\Choice(
         *     choices = {"fiction", "non-fiction"},
         *     message = "Оберіть валідний жанр."
         * )
         */
        private $genre;
    
        // ...
    }
    
  • Attributes
     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: 'Оберіть валідний жанр.',
        )]
        private $genre;
    
        // ...
    }
    
  • YAML
    1
    2
    3
    4
    5
    6
    # config/validator/validation.yaml
    App\Entity\Author:
        properties:
            genre:
                - Choice: { choices: [fiction, non-fiction], message: Choose a valid genre. }
            # ...
    
  • XML
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    <!-- config/validator/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
            https://symfony.com/schema/dic/constraint-mapping/constraint-mapping-1.0.xsd">
    
        <class name="App\Entity\Author">
            <property name="genre">
                <constraint name="Choice">
                    <option name="choices">
                        <value>fiction</value>
                        <value>non-fiction</value>
                    </option>
                    <option name="message">Оберіть валідний жанр.</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
    23
    // src/Entity/Author.php
    namespace App\Entity;
    
    // ...
    use Symfony\Component\Validator\Constraints as Assert;
    use Symfony\Component\Validator\Mapping\ClassMetadata;
    
    class Author
    {
        private $genre;
    
        // ...
    
        public static function loadValidatorMetadata(ClassMetadata $metadata)
        {
            // ...
    
            $metadata->addPropertyConstraint('genre', new Assert\Choice([
                'choices' => ['fiction', 'non-fiction'],
                'message' => 'Оберіть валідний жанр.',
            ]));
        }
    }
    

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

  • Annotations
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    // src/Entity/Author.php
    namespace App\Entity;
    
    // ...
    use Symfony\Component\Validator\Constraints as Assert;
    
    class Author
    {
        /**
         * @Assert\Choice({"fiction", "non-fiction"})
         */
        private $genre;
    
        // ...
    }
    
  • Attributes
     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;
    
        // ...
    }
    
  • YAML
    1
    2
    3
    4
    5
    6
    # config/validator/validation.yaml
    App\Entity\Author:
        properties:
            genre:
                - Choice: [fiction, non-fiction]
            # ...
    
  • XML
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    <!-- config/validator/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
            https://symfony.com/schema/dic/constraint-mapping/constraint-mapping-1.0.xsd">
    
        <class name="App\Entity\Author">
            <property name="genre">
                <constraint name="Choice">
                    <value>fiction</value>
                    <value>non-fiction</value>
                </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
    // src/Entity/Author.php
    namespace App\Entity;
    
    // ...
    use Symfony\Component\Validator\Constraints as Assert;
    use Symfony\Component\Validator\Mapping\ClassMetadata;
    
    class Author
    {
        private $genre;
    
        public static function loadValidatorMetadata(ClassMetadata $metadata)
        {
            // ...
    
            $metadata->addPropertyConstraint(
                'genre',
                new Assert\Choice(['fiction', 'non-fiction'])
            );
        }
    }
    

Така можливість дозволяє зробити налаштування найрозповсюдженіших опцій обмеження коротшим та швидшим.

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

Обмеження в класах форми

Обмеження можуть бути позначені під час побудови форм за допомогою опції constraints полів форми:

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 символи.

  • Annotations
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    // src/Entity/Author.php
    
    // ...
    use Symfony\Component\Validator\Constraints as Assert;
    
    class Author
    {
        /**
         * @Assert\NotBlank
         * @Assert\Length(min=3)
         */
        private $firstName;
    }
    
  • Attributes
     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;
    }
    
  • YAML
    1
    2
    3
    4
    5
    6
    7
    # config/validator/validation.yaml
    App\Entity\Author:
        properties:
            firstName:
                - NotBlank: ~
                - Length:
                    min: 3
    
  • XML
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    <!-- config/validator/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
            https://symfony.com/schema/dic/constraint-mapping/constraint-mapping-1.0.xsd">
    
        <class name="App\Entity\Author">
            <property name="firstName">
                <constraint name="NotBlank"/>
                <constraint name="Length">
                    <option name="min">3</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
    // src/Entity/Author.php
    namespace App\Entity;
    
    // ...
    use Symfony\Component\Validator\Constraints as Assert;
    use Symfony\Component\Validator\Mapping\ClassMetadata;
    
    class Author
    {
        private $firstName;
    
        public static function loadValidatorMetadata(ClassMetadata $metadata)
        {
            $metadata->addPropertyConstraint('firstName', new Assert\NotBlank());
            $metadata->addPropertyConstraint(
                'firstName',
                new Assert\Length(['min' => 3])
            );
        }
    }
    

Caution

Валідатор використовуватиме значення null якщо ініціалізація типізованої властивості була відмінена. Це може викликати неочікувану поведінку, якщо властивість містить значення при ініціалізації. Щоб уникнути цього, переконайтеся, що всі властивості ініціалізовані до їхньої валідації.

Геттери

Обмеження також можуть застосовуватися для того, щоб повернути значення методу. Symfony дозволяє вам додавати обмеження до будь-якого публічного методу, ім’я якого починається з «get», «is» або «has». У цій книзі подібні методи називаються загальним словом «геттери».

Перевагою цієї техніке є те, що вона дозволяє вам валідувати ваш об’єкт динамічно. Наприклад, уявіть, що вам потрібно переконатися, що поле пароля не співпадає з іменем користувача (з міркувань безпеки). Ви можете зробити це, створивши метод isPasswordLegal() та вказавши, що цей метод має повернутися як true:

  • Annotations
     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\IsTrue(message="Пароль не може співпадати з вашим ім'ям")
         */
        public function isPasswordSafe()
        {
            // ... повернути true або false
        }
    }
    
  • Attributes
     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: 'Пароль не може співпадати з вашим ім'ям')]
        public function isPasswordSafe()
        {
            // ... повернути true або false
        }
    }
    
  • YAML
    1
    2
    3
    4
    5
    # config/validator/validation.yaml
    App\Entity\Author:
        getters:
            passwordSafe:
                - 'IsTrue': { message: 'Пароль не може співпадати з вашим ім'ям' }
    
  • XML
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    <!-- config/validator/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
            https://symfony.com/schema/dic/constraint-mapping/constraint-mapping-1.0.xsd">
    
        <class name="App\Entity\Author">
            <getter property="passwordSafe">
                <constraint name="IsTrue">
                    <option name="message">Пароль не може співпадати з вашим ім'ям</option>
                </constraint>
            </getter>
        </class>
    </constraint-mapping>
    
  • 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;
    use Symfony\Component\Validator\Mapping\ClassMetadata;
    
    class Author
    {
        public static function loadValidatorMetadata(ClassMetadata $metadata)
        {
            $metadata->addGetterConstraint('passwordSafe', new Assert\IsTrue([
                'message' => 'Пароль не може співпадати з вашим ім'ям',
            ]));
        }
    }
    

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

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

Note

Найуважніші з вас помітили, що префікс геттера («get», «is» або «has») опущено при відображенні. Це дозволить вам застосувати обмеження до властивості з таким же ім’ям пізніше (або навпаки), не змінюючи логіку валідації.

Класи

Деякі обмеження застосовуються до цілого валідованого класу. Наприклад, обмеження Callback (зворотний виклик) - це універсальне обмеження, яке застосовується до самого класу. Коли цей клас валідується, методи, вказані обмеженням, просто виконуються, що дозволяє проводити більш вибіркову валідацію.

Налагодження обмежень

New in version 5.2: Команда debug:validator була представлена в Symfony 5.2.

Використайте команду 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" => "Це значення не має бути порожнім.",          |
    |               |                                                  |               |   "allowNull" => false,                                    |
    |               |                                                  |               |   "normalizer" => null,                                    |
    |               |                                                  |               |   "payload" => null                                        |
    |               |                                                  |               | ]                                                          |
    | firstArgument | Symfony\Component\Validator\Constraints\Email    | за            | [                                                          |
    |               |                                                  | замовчуванням |   "message" => "Це значення не валідна адреса email.", |
    |               |                                                  |               |   "mode" => null,                                          |
    |               |                                                  |               |   "normalizer" => null,                                    |
    |               |                                                  |               |   "payload" => null                                        |
    |               |                                                  |               | ]                                                          |
    +---------------+--------------------------------------------------+---------------+------------------------------------------------------------+

Ви також можете валідувати всі класи, що зберігаються у даному каталозі:

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

Висновки

Валідатор Symfony - це потужний інструмент, який використовується для отримання гарантій, що дані деякого об’єкта “валідні”. Сила валідації - в обмеженнях, які є правилами, які ви можете застосувати до властивостей або геттер-методів вашого об’єкта. І незважаючи на те, що ви більшою мірою використовуватимете фреймворк валідації опосередковано при використанні форм, пам’ятайте, що він може бути використаний де завгодно для валідації будь-якого об’єкта.

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