How do I work with help properties in form types?
I created a property ( foo
) on my form type. This property aims to pass the value from the method buildForm()
(where it was initialized and on the PRE_SUBMIT
listener where it was changed), to $view->vars
the buildView()
.
This is an example of what I am trying to do (without declaring a service):
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;
}
}
But this always returns the same value foo
for all form fields when I expected the opposite. The screenshot below displays CollectionType
from 3 FooType
:
I do not know why! Also, I tried to use options
some more when it doesn't make sense to me (as it is used internally only):
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefault('foo', rand(0, 100));
}
but I cannot figure out how to change this parameter in the methods of the current form.
How to do it right?
Thank!
source to share
The problem is caused by the natural state of the state of all types of forms (classes) in Symfony. However, if it is not declared as a service, each instance is created only once. So don't use properties for this kind of need unless you want some kind of stateful behavior like caching configuration.
Bypass
However, for each instance of the form type, a new instance of the form builder is created, so for these kinds of internal operations, you can use $builder->setAttribute()
instead.
Note. $builder->setAttribute()
does not apply to all HTML attributes, these are just form configuration attributes.
For example, the inline CollectionType
has an attribute prototype
with a similar situation:
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);
}
}
}
But for listeners, you don't have access to $builder->setAttribute()
, so you need an object value instead of a scalar one to be able to change the attribute. Something like this should work:
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;
}
// ...
}
Learn more about state or stateless state classes in this article .
source to share