Создание пользовательского отгадывателя типа

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

Создание пользовательского отгадывателя типа

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

Symfony также предоставляет некоторые отгадыватели типв в мостах:

Создайте отгадыватель типаPHPDoc

В этом разделе, вы построите отгадыватель, который читает информацию о полях из PHPDoc свойств. Вначале, вам нужно создать класс, реализующий FormTypeGuesserInterface. Этот интерфейс требует четыре метода:

guessType()
Пытается угадать тип поля;
guessRequired()
Пытается угадать значение обязательной опции;
guessMaxLength()
Пытается угадать значение атрибута ввода maxlength;
guessPattern()
Пытается угадать значение атрибута ввода pattern.

Начните с создание класса и этих методов. Далее, вы узнаете, как заполнять каждый из них:

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/Form/TypeGuesser/PHPDocTypeGuesser.php
namespace App\Form\TypeGuesser;

use Symfony\Component\Form\FormTypeGuesserInterface;
use Symfony\Component\Form\Guess\TypeGuess;
use Symfony\Component\Form\Guess\ValueGuess;

class PHPDocTypeGuesser implements FormTypeGuesserInterface
{
    public function guessType(string $class, string $property): ?TypeGuess
    {
    }

    public function guessRequired(string $class, string $property): ?ValueGuess
    {
    }

    public function guessMaxLength(string $class, string $property): ?ValueGuess
    {
    }

    public function guessPattern(string $class, string $property): ?ValueGuess
    {
    }
}

Отгадывание типа

При отгадывании типа, метод возвращает либо экземпляр класса TypeGuess, либо ничего, чтобы определить, что отгадыватель не может угадать тип.

Конструктор TypeGuess требует трёх опций:

  • Имя типа (один из типов форм);
  • Дополнительные опции (например, когда тип - entity, вам также надо установить опцию class). Если не угадан ни один тип, они должны быть установлены как пустой массив;
  • Уверенность в том, что угаданный тип - верный. Это может быть одной из констант класса Guess: LOW_CONFIDENCE, MEDIUM_CONFIDENCE, HIGH_CONFIDENCE, VERY_HIGH_CONFIDENCE. После того, как будут выполнены все отгадыватели типа, будет использован тип с наибольшим доверием.

Зная это, вы можете с лёгкостью реализовать метод guessType() в PHPDocTypeGuesser:

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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
// src/Form/TypeGuesser/PHPDocTypeGuesser.php
namespace App\Form\TypeGuesser;

use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
use Symfony\Component\Form\Extension\Core\Type\IntegerType;
use Symfony\Component\Form\Extension\Core\Type\NumberType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\Guess\Guess;
use Symfony\Component\Form\Guess\TypeGuess;

class PHPDocTypeGuesser implements FormTypeGuesserInterface
{
    public function guessType(string $class, string $property): ?TypeGuess
    {
        $annotations = $this->readPhpDocAnnotations($class, $property);

        if (!isset($annotations['var'])) {
            return null; // ничего не угадывать, если аннотация @var недоступна
        }

        // в обратном случае, основывайте тип на аннотации @var
        return match($annotations['var']) {
                // существует высокая уверенность в том, что тип - текст, когда
                // используется строка @var
            'string' => new TypeGuess(TextType::class, [], Guess::HIGH_CONFIDENCE),

                // целые числа также могут быть id сущности или флаговой кнопкой (0 или 1)
            'int', 'integer' => new TypeGuess(IntegerType::class, [], Guess::MEDIUM_CONFIDENCE),

            'float', 'double', 'real' => new TypeGuess(NumberType::class, [], Guess::MEDIUM_CONFIDENCE),

            'boolean', 'bool' => new TypeGuess(CheckboxType::class, [], Guess::HIGH_CONFIDENCE),

                // уверенность в том, что этот тип правильный, очень низкая
            default => new TypeGuess(TextType::class, [], Guess::LOW_CONFIDENCE)
        };
    }

    protected function readPhpDocAnnotations(string $class, string $property): array
    {
        $reflectionProperty = new \ReflectionProperty($class, $property);
        $phpdoc = $reflectionProperty->getDocComment();

        // разобрать $phpdoc в массив таким образом:
        // массив ('type' => 'string', 'since' => '1.0')
        $phpdocTags = ...;

        return $phpdocTags;
    }

    // ...
}

Этот отгадыватель типа теперь может угадывать типа поля для свойства, если оно имеет PHPdoc!

Опции поля отгадывания

Три других метода (guessMaxLength(), guessRequired() и guessPattern()) возвращают экземпляр ValueGuess с значением опции. Этот конструктор имеет 2 аргумента:

  • Значение опции;
  • Уверенность в том, что отгаданное значение - верное (используя константы класса Guess).

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

Caution

Вы должны быть очень осторожны используя метод guessPattern(). Когда тип - float, вы не можете использовать его для определения минимального или максимального значения float (например, вы хотите, чтобы float был больше, чем 5, 4.512313 - не является валидным, но length(4.512314) > length(5) - валидно, так что схема сработает). В этом случае, значение должно быть установлено, как null с MEDIUM_CONFIDENCE.

Регистрация отгадывателя типа

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

Если вы не используете автомонтирование и автоконфигурацию, зарегистрируйте ваш сервис вручную, и тегируйте его с помощью form.type_guesser:

  • YAML
  • XML
  • PHP
1
2
3
4
5
6
# config/services.yaml
services:
    # ...

    App\Form\TypeGuesser\PHPDocTypeGuesser:
        tags: [form.type_guesser]

Если вы используете компоненты Формы изолированно в вашем PHP-проекте, используйте addTypeGuesser() или addTypeGuessers() в FormFactoryBuilder, чтобы зарегистрировать новые отгадыватели типа:

1
2
3
4
5
6
7
8
9
use Symfony\Component\Form\Forms;
use Acme\Form\PHPDocTypeGuesser;

$formFactory = Forms::createFormFactoryBuilder()
    // ...
    ->addTypeGuesser(new PHPDocTypeGuesser())
    ->getFormFactory();

// ...

Tip

Выполните следующую команду, чтобы верифицировать, что отгадыватель типа формы был успешно зарегистирован в приложении:

1
$ php bin/console debug:form