Как обращаться со свойствами справки в типах форм?


Я создал свойство (foo) в моем типе формы. Это свойство имеет целью передать значение из метода buildForm() (где оно инициализировано и на слушателе PRE_SUBMIT, где оно модифицировано) в $view->vars в buildView().

Вот пример того, что я пытаюсь сделать (без объявления службы):

class FooType extends AbstractType
{
    private $foo;

    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $this->foo = rand(0, 100);

        $builder->addEventListener(FormEvents::PRE_SUBMIT, function (FormEvent $event) {
            $this->foo = rand(0, $event->getData());
        });
    }

    public function buildView(FormView $view, FormInterface $form, array $options)
    {
        $view->vars['foo'] = $this->foo;
    }

    public function getParent()
    {
        return NumberType::class;
    }
}
Но это всегда возвращает одно и то же значение foo для всех полей формы, когда я ожидал обратного. На следующем скриншоте показано CollectionType с 3 FooType записи:

скриншот

Я не знаю, почему! Кроме того, я пытался использовать options еще тогда, когда это не имеет смысла для меня (так как он используется только для внутренних целей):

public function configureOptions(OptionsResolver $resolver)
{
    $resolver->setDefault('foo', rand(0, 100));
}

Но я не могу понять, как изменить эту опцию в текущих методах формы.

Как добиться этого правильно?

Спасибо!

1 3

1 ответ:

Проблема вызвана естественным состоянием всех типов форм (классов) в Symfony. Тем не менее, когда он не объявлен как Служба, каждый экземпляр создается только один раз. Поэтому не используйте свойства для такого рода потребностей, если вы не хотите некоторого поведения с сохранением состояния, такого как конфигурация кэширования.

Обходной путь

Однако для каждого экземпляра типа формы создается новый экземпляр построителя форм, поэтому для этих видов внутренних операций можно использовать $builder->setAttribute() вместо этого.

Примечание: $builder->setAttribute() речь идет не об атрибутах HTML вообще, а только об атрибутах конфигурации формы.

Например, встроенный CollectionType имеет атрибут prototype с аналогичной ситуацией:

namespace Symfony\Component\Form\Extension\Core\Type;

// ...

class CollectionType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {   
        if ($options['allow_add'] && $options['prototype']) {
            // ...

            $builder->setAttribute('prototype', $prototype->getForm());
        }
    }

    public function buildView(FormView $view, FormInterface $form, array $options)
    {
        // ...

        if ($form->getConfig()->hasAttribute('prototype')) {
            $prototype = $form->getConfig()->getAttribute('prototype');
            $view->vars['prototype'] = $prototype->setParent($form)->createView($view);
        }
    }
}

Но для прослушивателей у вас нет доступа к $builder->setAttribute(), поэтому требуется значение объекта вместо скалярного, чтобы иметь возможность изменять атрибут. Что-то вроде этого должно сработать:

class FooType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {   
        $builder->setAttribute('dummy', (object) array(
            'foo' => rand(0, 100),
        ));

        $builder->addEventListener(FormEvents::PRE_SUBMIT, function (FormEvent $event) {
            $dummy = $event->getForm()->getConfig()->getAttribute('dummy');
            $dummy->foo = rand(0, $event->getData());
        });
    }

    public function buildView(FormView $view, FormInterface $form, array $options)
    {
        $view->vars['foo'] = $form->getConfig()->getAttribute('dummy')->foo;
    }

    // ...
}

Подробнее о классах с сохранением состояния или без сохранения состояния см. статья .