Symfony表格状态备份到数据库并还原 /或:如何从数据库中预填充表单



我在其中有一个很大的形式,其中有时间,日期,选择和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
            }
        }
    }
}

我很确定此解决方案将节省某人的编码!

相关内容

  • 没有找到相关文章