Как и когда использовать отображатели данных
Дата обновления перевода 2025-08-25
Как и когда использовать отображатели данных
Когда форма составная, изначальные данные должны быть переданы дочерним, чтобы каждая могла отображать собственное значение ввода. При отправке, значения дочерних форм должны быть записаны обратно в форму.
Отображатели данных отвечают за чтение и запись данных из/в родительскую форму.
Основной встроенный отображатель данных использует компонент PropertyAccess, который подойдет в большинстве случаев. Однако, вы можете создать собственную реализацию, которая может, к примеру, передавать отправленные данные постоянным объектам через свой конструктор.
Разница между отображателями и преобразователями данных
Важно знать разницу между преображателями данных и отображателями.
- Преображатели данных изменяют представление значения (например, из
"2016-08-12"в экземплярDateTime); - Отображатели данных отображают данные (например, объект или массив) в
полях форм и обратно, например, используя один экземпляр 
DateTimeдля наполнения внутренних полей (например, года, часа и т.д.) составного типа даты. 
Создание отображателя данных
Представьте, что вы хотите сохранить набор цветов в базе данных. Для этого, вы используете постоянный объект цвета:
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
// src/Painting/Color.php
namespace App\Painting;
final class Color
{
    public function __construct(
        private int $red,
        private int $green,
        private int $blue,
    ) {
    }
    public function getRed(): int
    {
        return $this->red;
    }
    public function getGreen(): int
    {
        return $this->green;
    }
    public function getBlue(): int
    {
        return $this->blue;
    }
}
    Тип формы должен иметь разрешение на изменение цвета. Н так как вы решили сделать
объект Color постоянным, новый объект цвета должен быть создан каждый раз,
когда изменяетя одно из значений.
Tip
Если вы используете изменяемые объект с аргументами конструктора, вместо
использования отображателя данных, вам нужно сконфигурировать опцию empty_data
с замыканием, как описывается в статье
Как сконфигурировать пустые данные для класса формы .
Поля формы красный, зеленый и синий должны быть отображены аргументам конструктора,
а экземпляр Color должен быть отображен полям формы красный, зеленый и голубой.
Узнаёте знакомый паттерн? Пришло время отображателя данных. Самый простой создать его
- реализовать DataMapperInterface в вашем типе формы:
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/ColorType.php
namespace App\Form;
use App\Painting\Color;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\DataMapperInterface;
use Symfony\Component\Form\Exception\UnexpectedTypeException;
use Symfony\Component\Form\FormInterface;
final class ColorType extends AbstractType implements DataMapperInterface
{
    // ...
    /**
     * @param Color|null $viewData
     */
    public function mapDataToForms($viewData, \Traversable $forms): void
    {
        // здесь еще нет данных, поэтому нечего предварительно наполнять
        if (null === $viewData) {
            return;
        }
        // невалидный тип данных
        if (!$viewData instanceof Color) {
            throw new UnexpectedTypeException($viewData, Color::class);
        }
        /** @var FormInterface[] $forms */
        $forms = iterator_to_array($forms);
        // инициализировать значения полей формы
        $forms['red']->setData($viewData->getRed());
        $forms['green']->setData($viewData->getGreen());
        $forms['blue']->setData($viewData->getBlue());
    }
    public function mapFormsToData(\Traversable $forms, &$viewData): void
    {
        /** @var FormInterface[] $forms */
        $forms = iterator_to_array($forms);
        // так как данные передаются по ссылке, переопределение изменит их и в
        // объекте формы
        // не забывайте о несовместимости типов, см. предупреждение ниже
        $viewData = new Color(
            $forms['red']->getData(),
            $forms['green']->getData(),
            $forms['blue']->getData()
        );
    }
}
    Warning
Данные, переданные отображателю, еще не валидированы. Это означает, что ваши объекты должны позволять свое создание в невалидном состоянии, чтобы предоставить дружелюбные по отношению к пользователю ошибки в форме.
Использование отображателя
После создания отображателя данных, вам нужно сконфигурировать форму для его использования. Этого можно достичь используя метод setDataMapper():
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
// src/Form/Type/ColorType.php
namespace App\Form\Type;
// ...
use Symfony\Component\Form\Extension\Core\Type\IntegerType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
final class ColorType extends AbstractType implements DataMapperInterface
{
    public function buildForm(FormBuilderInterface $builder, array $options): void
    {
        $builder
            ->add('red', IntegerType::class, [
                // форсировать строгость типа, чтобы гарантировать, что конструктор
                // класса Color не сломается
                'empty_data' => '0',
            ])
            ->add('green', IntegerType::class, [
                'empty_data' => '0',
            ])
            ->add('blue', IntegerType::class, [
                'empty_data' => '0',
            ])
            // сконфигурировать отображатель данных для этого FormType
            ->setDataMapper($this)
        ;
    }
    public function configureOptions(OptionsResolver $resolver): void
    {
        // при создании нового цвета, изначальные данные должны быть null
        $resolver->setDefault('empty_data', null);
    }
    // ...
}
    Круто! При использовании формы ColorType, пользовательские методы отображателя
данных теперь создадут новый объект Color.
Отображение полей формы с использованием обратных вызовов
Удобно, что вы также можете отображать данные из и в поле формы, используя
опции getter и setter. Например, представьте, что у вас есть форма
с некоторыми полями, и только одно из них должно быть отображено каким-то особым
образом. Или вам нужно изменить то, как оно записывается в подлежащий объект.
В таком случае, зарегистрируйте PHP-вызываемое, которое может писать или читать
из/в этот конкретный объект:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
public function buildForm(FormBuilderInterface $builder, array $options)
{
    // ...
    $builder->add('state', ChoiceType::class, [
        'choices' => [
            'active' => true,
            'paused' => false,
        ],
        'getter' => function (Task $task, FormInterface $form): bool {
            return !$task->isCancelled() && !$task->isPaused();
        },
        'setter' => function (Task &$task, bool $state, FormInterface $form): void {
            if ($state) {
                $task->activate();
            } else {
                $task->pause();
            }
        },
    ]);
}
    Если они доступны, эти опции имеют главенство над свойством метода пути и отображатель данных по умолчанию все еще будет использовать компонент PropertyAccess для других полей формы.
Warning
Когда форма имеет опцию inherit_data установленную, как true, она
не использует отображатель данных и позволяет родительской форме отображать
внутренние значения.