EntityType choice_value с составленным первичным ключом и событием setData
Мой пользовательский тип формы основан на EntityType
,
Моя сущность имеет составной первичный ключ, поэтому я установил choice_value
отразить значения обоих ключевых полей в опциях "value"
атрибут, который работает как ожидалось.
Я подписался на formType PRE_SUBMIT
событие, чтобы я мог разобрать это значение и преобразовать его обратно в экземпляр сущности спасибо Doctrine\ORM\EntityManager::getReference
метод.
Моя проблема возникает при подаче. Я ожидал событие setData($my_retrieved_entity)
метод (внутри прослушивателя событий), чтобы успешно заменить представленное значение формы соответствующей сущностью (удовлетворяющей конвейеру проверки формы), но вместо этого я получаю то, что кажется сообщением об ошибке Symfony по умолчанию:
"Это значение недействительно."
// …
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setRequired(['entityManager','country']);
$resolver->setAllowedTypes('country', Country::class);
$resolver->setAllowedTypes('entityManager', EntityManager::class);
$resolver->setDefaults([
'class' => NetworkTypeModel::class,
'placeholder' => 'Choose a network type',
'choice_value' => function ($networkType) {
// Set specific format for for the value attribute
// so that it reflects both primary key fiels values
return $networkType
? "{$networkType->getId()}¤{$networkType->getCountry()->getId()}"
: ''
;
}
,'query_builder' => function (Options $options) {
return function (EntityRepository $er) use ($options) {
$qb = $er->createQueryBuilder('nt')
->leftJoin('nt.country', 'c')
->addSelect('c');
return $qb->where($qb->expr()->eq('nt.country', ':country'))
->setParameter('country', $options['country']->getId())
->orderBy('nt.label', 'ASC');
};
},
]);
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
$entityManager = $options['entityManager'];
// Listen to post DATA in order to transform Option's value
// back to a networkType instance
$builder->addEventListener(
FormEvents::PRE_SUBMIT,
function (FormEvent $event) use ($entityManager) {
if ($data = $event->getData()) {
// Special value has been sent through POST field
// It needs to be parsed and transformed back to a networkType entity
// According to the format set from inside choice_value options.
$ids = explode('¤', $data);
$networkType = $entityManager->getReference(
NetworkTypeModel::class,
['id' => $ids[0], 'country' => $ids[1]]
);
// I expect $event->setData to populate the form submitted value
// to be the selected value
$event->setData($networkType);
// But I get the following common error message on that field
// «This value is not valid.»
}
}
);
}
public function getParent()
{
return EntityType::class;
}
Тем не менее, выполнение dump($networkType)
прямо перед $event->setData($networkType);
выводит совершенно правильный экземпляр сущности и фактически правильный, связанный с отправленным элементом.
Такое чувство, что я почти все понял правильно, но я понятия не имею, что здесь не так. Любая идея о том, как правильно преобразовать представленные данные в объект, внедрить их в форму и удовлетворить цепочку валидатора?
РЕДАКТИРОВАТЬ
Я выполнил свою работу, но, вероятно, не совсем в стиле Symfony Это может помочь вам понять мою потребность.
Я использовал общую переменную:
- Населенный изнутри
PRE_SUBMIT
прослушиватель событий с ожидаемым экземпляром networkType, восстановленный с использованием$event->getData()
разобранная строка. - Затем потребляется этот экземпляр ссылки внутри
POST_SUBMIT
событие какFormEvent::setData
аргумент.
Изменен / добавлен код:
$entityManager = $options['entityManager'];
$selectedNetworkType = null;
// Transform Option's value back to a networkType instance
$builder->addEventListener(
FormEvents::PRE_SUBMIT,
function (FormEvent $event) use ($entityManager, &$selectedNetworkType) {
// Transform the custom dropdown "value" attribute coming from POST
// into a my networkType model instance
if ($data = $event->getData()) {
// PRE_SUBMIT event data holds the view data as a string
// which needs to be parsed accordingly to what have been
// done when encoding the entity's ids in choice_value callable
$ids = explode('¤', $data);
// We don't need to retrieve the entire record from DB anyway
// so we use getReference
$networkType = $entityManager->getReference(
NetworkTypeModel::class,
['id' => $ids[0], 'country' => $ids[1]]
);
// Now store the newly created networkType instance
// for later
$selectedNetworkType = $networkType;
}
}
)->addEventListener(
FormEvents::POST_SUBMIT,
function (FormEvent $event) use (&$selectedNetworkType) {
// Use the fresh stored instance to feed the model data
$selectedNetworkType && $event->setData($selectedNetworkType);
}
);
Я надеюсь, что кто-то придумает правильные указания для решения этого варианта использования, для лучшего понимания компонента Symfony Form.
Спасибо.
1 ответ
Насколько я понял, ваша задача состоит в том, чтобы изменить сущность, которую ваша форма собирает из данных формы, непосредственно перед тем, как она будет отправлена обратно клиенту. Вам на самом деле нужно использовать FormEvents::SUBMIT, чтобы сделать это ($event->setData($data) и $event->getData(), которые здесь используются для управления пересобранной сущностью здесь), и что вы делаете, это изменяете массив необработанных данных формы (который вы получите с помощью вызова $event->getData() внутри FormEvents::PRE_SUBMIT, вы можете проверить это, вызвав var_dump() для него) для прокси-объекта, который при отправке формы пытается воссоздать вашу сущность из прокси-сервера (пока он действителен) данные для отправки должны быть предоставлены в виде массива). Также обратите внимание, что вы используете другой экземпляр диспетчера сущностей, кроме самой формы, поэтому ваша сущность, которую вы получите из формы, не будет управляться. Таким образом, лучшим подходом будет изменение измененной сущности, возвращаемое $event->getData() внутри события submit, но я не чувствую, что вы получаете его из-за составного ключа. Также обратите внимание, что вы можете получить данные формы в событии submit, вызвав его таким образом
$event->getForm()->get('id')->getData();
Это было не ясно для меня, когда я впервые столкнулся с такой проблемой, поэтому может быть полезным для вас.