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

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

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

use Symfony\Component\Form\FormTypeGuesserInterface;

class PHPDocTypeGuesser implements FormTypeGuesserInterface
{
    public function guessType($class, $property)
    {
    }

    public function guessRequired($class, $property)
    {
    }

    public function guessMaxLength($class, $property)
    {
    }

    public function guessPattern($class, $property)
    {
    }
}

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

При отгадывании типа, метод возвращает либо экземпляр класса 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
53
54
55
56
57
58
59
60
namespace AppBundle\Form\TypeGuesser;

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

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

        if (!isset($annotations['var'])) {
            return; // guess nothing if the @var annotation is not available
        }

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

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

            case 'float':
            case 'double':
            case 'real':
                return new TypeGuess(NumberType::class, array(), Guess::MEDIUM_CONFIDENCE);

            case 'boolean':
            case 'bool':
                return new TypeGuess(CheckboxType::class, array(), Guess::HIGH_CONFIDENCE);

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

    protected function readPhpDocAnnotations($class, $property)
    {
        $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
    1
    2
    3
    4
    5
    6
    # app/config/services.yml
    services:
        # ...
    
        AppBundle\Form\TypeGuesser\PHPDocTypeGuesser:
            tags: [form.type_guesser]
    
  • XML
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    <!-- app/config/services.xml -->
    <?xml version="1.0" encoding="UTF-8" ?>
    <container xmlns="http://symfony.com/schema/dic/services"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://symfony.com/schema/dic/services
            http://symfony.com/schema/dic/services/services-1.0.xsd">
    
        <services>
            <service id="AppBundle\Form\TypeGuesser\PHPDocTypeGuesser">
                <tag name="form.type_guesser"/>
            </service>
        </services>
    </container>
    
  • PHP
    1
    2
    3
    4
    5
    6
    // app/config/services.php
    use AppBundle\Form\TypeGuesser\PHPDocTypeGuesser;
    
    $container->register(PHPDocTypeGuesser::class)
        ->addTag('form.type_guesser')
    ;
    

New in version 3.3: До Symfony 3.3, вам нужно было определять сервисы отгадывателя типа, как public. Начиная с Symfony 3.3, вы также можете определять их, как private.

Если вы используете компоненты Формы изолированно в вашем 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();

// ...

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