我在其中有一个很大的形式,其中有时间,日期,选择和entityType字段。该表格与实体没有链接,但确实包含来自其他实体的字段。换句话说,我在FormType的optionsResolver中没有data_class
。
这是我的formType :(仅显示一个字段才能简单)
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('company', EntityType::class, array(
'placeholder' => 'Companies',
'class' => 'AppBundle:Company',
'choice_label' => 'name',
'multiple' => true,
'query_builder' => function (EntityRepository $repository) {
return $repository->createQueryBuilder('c')->orderBy('c.name', 'ASC');
},
'required' => false
))
//... much more fields
}
public function configureOptions(OptionsResolver $resolver)
{
// This form is not linked to an Entity
// Therefore no `data_class`
}
在控制器中,我可以备份表单的数据。称为FormState。我将FormState保存到数据库中,如下所示:
namespace AppBundleController;
class ReportController extends Controller
{
/** // ... */
public function listAction(Request $request)
{
$form = $this->createForm(ReportType::class);
$form->handleRequest($request);
// Save the form to a FormState
if ($form->isSubmitted() && $form->getClickedButton()->getName() == 'saveFormState') {
$formStateManager = $this->get('app.manager.form_state');
// Get form data that we want to save
$data = $form->getData();
$name = 'Stackoverflow example'; // give it a name
$formState = $formStateManager->createNewFormState(); // Create new FormState Entity object
$formState->setName( $name );
$formState->setData( $data );
$formStateManager->save( $formState );
}
// ...
}
}
上面的所有这些都非常完美。但是现在棘手的部分,我必须将备份返回到表单。用户可以从形式列表中选择先前的表单状态。然后按负载按钮。
我尝试加载到表单的FormState对象的data
属性只是$form->getData()
结果。因此,一个没有对象的普通数组。也许那是问题所在,但我无法使它起作用。
data
数组是我试图将其加载到表单中的内容,因此它将其值重新接管。我通过$form->setData()
或使用$this->createForm(ReportType::class, $data)
创建新表单尝试了一下。两者都因相同的错误消息而失败:Entities passed to the choice field must be managed. Maybe persist them in the entity manager?
。
我尝试了两种添加数据的方法:
第一次尝试,在控制器中:
namespace AppBundleController;
class ReportController extends Controller
{
/** // ... */
public function listAction(Request $request)
{
if ($form->isSubmitted() && $form->getClickedButton()->getName() == 'loadFormState') {
// ...
$form = $this->createForm(ReportType::class, $formState->getData()); // <-- throws the error
$form->submit($formState->getData());
}
}
}
第二次尝试,通过FormEvent订阅者:当我通过一个格式订阅者(如下)这样做时,我会遇到相同的错误。这是我的代码:
namespace AppBundleFormEventListener;
class ReportFieldsSubscriber implements EventSubscriberInterface
{
// ...
public static function getSubscribedEvents()
{
return array(
FormEvents::PRE_SUBMIT => array(
array('loadFormState'),
),
);
}
/**
* Load selected FormState in form
* @param FormEvent $event
*/
public function loadFormState(FormEvent $event)
{
$form = $event->getForm();
//...
// Choosen FormState
$formState = $formStateManager->findOneById($formStateId);
$formStateData = $formState->getData();
$form->setData($formStateData); // <-- Set data, trows error
// ...
}
}
正如我所说的两个解决方案都抛出了错误: Entities passed to the choice field must be managed. Maybe persist them in the entity manager?
在这种情况下,首选解决方案是什么?我可以创建一个虚拟实体,并解决此问题,但感觉有些丑陋,而不是要走的路。
在摆弄几天后,我找到了解决方案。
我的挣扎不是如何保存形式数据,如我在问题中所示。但是,我通过将查看数据保存到数据库中,然后尝试加载该数据。在这里有一个很好的阅读:https://symfony.com/doc/current/form/form/events.html#registering-event-listeners-listeners-or-vent-subscribers
因此,我还重写了形式状态以订阅表格的保存。注意:请记住,在FormType的OptionsResolver设置中,我的表格没有data_class
。因此,没有自动数据映射等。
在我的最初问题中,您可以看到我有多个提交按钮,在控制器中,您可以轻松单击哪个按钮。因为在控制器中,您可以访问getClickedButton()
功能。但不以订户形式。因此,我找到了一种在表单的EventBscriberFace中获取单击按钮的方法:
class ReportFieldsSubscriber implements EventSubscriberInterface
{
/**
* Find which button was clicked
* @param $data
* @return string
*/
public function getClickedButton($data)
{
$requestParams = serialize($data);
if (strpos($requestParams, 'saveFormState') !== false) {
return 'saveFormState';
}
if (strpos($requestParams, 'loadFormState') !== false) {
return 'loadFormState';
}
if (strpos($requestParams, 'deleteFormState') !== false) {
return 'deleteFormState';
}
return "";
}
}
在下面,您找到了我如何以正确的方式保存和加载形式的实际解决方案。我在formevent :: pre_submit阶段将它们连接起来。在那个阶段,您可以将请求数据保存并加载到FormEvent中。完美。
通过formevent :: pre_submit:
保存和加载/预填充表单数据class ReportFieldsSubscriber implements EventSubscriberInterface
{
public static function getSubscribedEvents()
{
return array(
FormEvents::PRE_SUBMIT => array(
array('saveFormState'),
array('loadFormState'),
),
);
}
/**
* Save form data
* @param FormEvent $event
*/
public function saveFormState(FormEvent $event)
{
$data = $event->getData();
if ($this->getClickedButton($data) == 'saveFormState') {
$formStateManager = $this->formStateManager;
// ...
$formState = $formStateManager->createNewFormState();
// ...
$formState->setData($data);
$formStateManager->save($formState);
}
}
/**
* Load choosen form data
* @param FormEvent $event
*/
public function loadFormState(FormEvent $event)
{
$data = $event->getData();
if ($this->getClickedButton($data) == 'loadFormState') {
$formStateId = $data['formState']['formStates'];
if (is_numeric($formStateId)) {
$formStateManager = $this->formStateManager;
$formState = $formStateManager->findOneById(intval($formStateId));
// Choosen FormState data
$formStateData = $formState->getData();
$event->setData($formStateData); // <-- Call setData() on the FormEvent, not on the form itself
}
}
}
}
我很确定此解决方案将节省某人的编码!