Я привязываюсь к узлам, пытаясь бороться с создателями форм, событиями и трансформаторами Symfony2... надеюсь, кто-то здесь более опытен и может помочь!
У меня есть поле формы (выберите раскрывающийся список), который содержит некоторые значения (краткий список), который сопоставляется с Entity. Один из этих вариантов - "другой". Предположим, что AJAX пока нет, и когда пользователь отправляет форму, я хочу определить, выбрали ли они "другое" (или любой другой вариант, не входящий в список). Если они выбрали один из этих вариантов, то должен быть показан полный список опций, иначе просто покажите краткий список. Должно быть легко, не так ли?;)
Итак, у меня есть свой тип формы, и он отображает основной список как раз отлично. Код выглядит примерно так:
namespace Company\ProjectBundle\Form\Type;
use ...
class FancyFormType extends AbstractType {
private $fooRepo;
public function __construct(EntityManager $em, FooRepository $fooRepo)
{
$this->fooRepo = $fooRepo;
}
public function buildForm(FormBuilderInterface $builder, array $options) {
/** @var Bar $bar */
$bar = $builder->getData();
$fooTransformer = new FooToStringTransformer($options['em']);
$builder
->add($builder
->create('linkedFoo', 'choice', array(
'choices' => $this->fooRepo->getListAsArray(
$bar->getLinkedfoo()->getId()
),
))
->addModelTransformer($fooTransformer)
)
;
// ...
}
// ...
}
Теперь я хочу проверить представленное значение, поэтому я использую прослушиватель форм событий следующим образом.
public function buildForm(FormBuilderInterface $builder, array $options) {
// ... This code comes just after the snippet shown above
$builder->addEventListener(FormEvents::PRE_SUBMIT, function(FormEvent $event) {
/** @var EntityManager $em */
$em = $event->getForm()->getConfig()->getOption('em');
$data = $event->getData();
if (empty($data['linkedFoo'])) return;
$selectedFoo = $data['linkedfoo'];
$event->getForm()->add('linkedFoo', 'choice', array(
'choices' => $em
->getRepository('CompanyProjectBundle:FooShortlist')
->getListAsArray($selectedFoo)
,
));
//@todo - needs transformer?
});
}
Однако он не работает с сообщением об ошибке:
Notice: Object of class Proxies\__CG__\Company\ProjectBundle\Entity\Foo could not be converted to int in \path\to\project\symfony\symfony\src\Symfony\Component\Form\Extension\Core\ChoiceList\ChoiceList.php line 458
Я предполагаю эту ошибку потому, что, когда linkedFoo
был переписан, он удалил modelTransformer
? Я пробовал различные способы доступа к строителю при закрытии события, но это, похоже, не работало (возвращаемые значения были неожиданными). Есть ли другой метод, который я должен использовать в случае, отличном от $event->getForm()->add()
? Или есть более фундаментальная проблема с моим подходом здесь?
В принципе, я не хочу вмешиваться в конфигурацию/трансформаторы/метки полей linkedFoo
, кроме как изменить доступные варианты... есть ли другой способ сделать это? Например. что-то вроде $form->getField()->updateChoices()
?
Заранее благодарим за любую помощь, которую вы можете предложить!
С
P.S. есть ли более хорошая документация или обсуждение форм, событий и т.д., чем на веб-сайте Symfony? Например. какая разница между PRE_SET_DATA, PRE_SUBMIT, SUBMIT и т.д.? Когда они увольняются? Для чего они должны использоваться? Как наследование работает с полями пользовательских форм? Что такое форма и строитель, как они взаимодействуют, и когда вы должны иметь дело с каждым? Как, когда и почему вы должны использовать FormFactory, вы можете получить доступ через $form->getConfig()->getFormFactory()
? Etc..
Изменить: В ответ на предложение Флориана здесь появилась дополнительная информация о вещах, которые были опробованы, но не работают:
Если вы попытаетесь получить FormBuilder внутри события следующим образом:
/** @var FormBuilder $builder */
$builder = $event->getForm()->get('linkedFoo')->getConfig();
$event->getForm()->add($builder
->create('linkedFoo', 'choice', array(
'choices' => $newChoices,
'label' =>'label',
))
->addModelTransformer(new FooToStringTransformer($em))
);
Затем вы получите сообщение об ошибке:
FormBuilder methods cannot be accessed anymore once the builder is turned
into a FormConfigInterface instance.
Итак, вы попробуете что-то вроде того, что предложил Флориан, т.е.
$event->getForm()->add('linkedFoo', 'choice', array(
'choices' => $newChoices,
));
$event->getForm()->get('linkedFoo')->getConfig()->addModelTransformer(new FooToStringTransformer($em));
... но вместо этого вы получите эту ошибку:
Notice: Object of class Proxies\__CG__\Company\ProjectBundle\Entity\Foo could not be converted to int
in C:\path\to\vendor\symfony\symfony\src\Symfony\Component\Form\Extension\Core\ChoiceList\ChoiceList.php line 458
Кажется, что вторая строка (которая добавляет ModelTransformer) никогда не вызывается, потому что вызов ->add()
терпит неудачу, прежде чем вы сможете туда добраться.