我的自定义表单类型基于EntityType
.
我的实体有一个复合主键,因此我将其设置choice_value
为反映 options"value"
属性中的两个键字段值,这可以按预期工作。
我订阅了 formTypePRE_SUBMIT
事件,以便我可以解析该值并将其转换回实体实例,感谢 toDoctrine\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 组件。
谢谢你。