Symfony 3 表单集合实体带有文件类型字段的编辑动作

3
如何正确处理表单更新,当实体集合中有fileType字段时。 我根据Symfony上传文档进行了操作和侦听器。实体创建完美运行,但编辑操作失败,因为没有选择文件,而Symfony尝试在文件字段上更新集合实体的空值。
AppBundle\Entity\Product:
    type: entity
    # ...
    oneToMany:
        images:
            targetEntity: Image
            mappedBy: product

表单:

// AppBundle\Form\ProductType
public function buildForm(FormBuilderInterface $builder, array $options)
{
    $builder
        // ...
        ->add(
            'images',
            CollectionType::class,
            [
                'entry_type'    => ImageType::class,
                'allow_add'     => true,
                'allow_delete'  => true,
                'by_reference'  => false,
                'entry_options' => ['label' => false],
                'label_attr'    => [
                    'data-feature' => 'editable',
                ],
            ]
        );
}

// AppBundle\Form\ImageType   
public function buildForm(FormBuilderInterface $builder, array $options)
{
    $builder
        ->add('file', FileType::class, ['required' => false])
        // another fields...
}

动作:

// AppBundle\Controller\Backend\ProductController
// ...
public function editAction(Request $request, EntityManagerInterface $em, Product $product)
{
    $editForm = $this->createForm('AppBundle\Form\ProductType', $product);
    $editForm->handleRequest($request);

    $originalImages = new ArrayCollection();

    foreach ($product->getImages() as $image) {
        $originalImages->add($image);
    }

    if ($editForm->isSubmitted()) {
        if ($editForm->isValid()) {
            foreach ($originalImages as $image) {
                if (false === $product->getImages()->contains($image)) {
                    $em->remove($image);
                }
            }

            $em->flush();

            $this->addFlash('success', 'Success');
        } else {
            $this->addFlash('warning', 'Error saving');
        }

        return $this->redirectToRoute('backend_product_edit', ['id' => $product->getId()]);
    }
}
// ...

我认为我需要在某个地方取消设置空文件字段,但我不知道在哪里...

P.S. 我知道我可以使用像VichUploaderBundle这样的捆绑包,但我想要了解它是如何工作的,以及我做错了什么!

P.P.S. 抱歉我的英语不好。

2个回答

3
修改ImageType表单完全解决了我的问题。
// AppBundle\Form\ImageType   
public function buildForm(FormBuilderInterface $builder, array $options)
{
    $builder
        ->add('file', FileType::class, ['required' => false, 'data_class' => null]])
        // another fields...
    ;
    // adding this forces to use old file if there is no file uploaded
    $builder->get('file')->addEventListener(FormEvents::PRE_SUBMIT, function (FormEvent $event) {
        if (null === $event->getData()) {
            $event->setData($event->getForm()->getData());
        }
    });
}

2
一周前我也遇到了同样的问题。在阅读了许多关于这个问题的帖子后,似乎出于安全原因,您不能预先填充表单中的“文件字段”。
我实现的简单解决方案是:在您的“editAction”方法中,在呈现“edit”表单之前将当前文件字段的值分配给用户会话。
然后,当用户提交“update”表单时,您可以使用预更新事件(例如Doctrine生命周期)在会话中查找那些文件名,并在持久化之前将它们重新分配给您的实体。
基本上,您应该像这样做(我为我的实体提供代码,您需要根据自己的逻辑进行调整):
在我的情况下,Doctrine关系是1个新闻对应1个图像文件。
在您的editAction中:
protected function editNewsAction()
{
    //some code 

    if (!$response instanceof RedirectResponse)
    {
        $entityId = $this->request->query->get('id'); //the id of my entity was in the query string in my logic

        $repo = $this->getDoctrine()->getManager()->getRepository('AppBundle:News');
        $oldEntity = $repo->findOneBy(['id' => $entityId]);
        $oldEntityImage = $oldEntity->getImage(); // I had oneToOne Relation so adapt it for your own logic

        $session = $this->get('session');
        $session->set('newsImage', $oldEntityImage);
    }

    // some code
}

然后在预更新事件中:

protected function preUpdateNewsEntity($entity)
{
    //some code

    if (!$entity->getImageFile())
    {
        $session = $this->get('session');

        $entity->setImage($session->get('newsImage'));

        //don't forget to remove the value in session
        $session->remove('newsImage');
    }

    //some code
}

我希望这能对你有所帮助。

我考虑过类似这样的东西,但是使用 FormEvents::PRE_SET_DATA 事件监听器。有可能获取原始对象并检查提交的字段,在文档中有更多信息。 - sadikoff
感谢提供链接。我认为你是完全正确的。FormEvents 也可以使用。 - Mz1907

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接