Symfony2:如果没有提供,不要更新表单字段

10
我有一个针对“Team”实体的表单。该实体具有一个“image”字段。在创建过程中这个字段是必填项,但在编辑过程中就不是了。但现在,在编辑过程中,如果我没有提供文件输入中的任何图像,空输入仍将被保留,因此在该过程中我的数据库字段被清空。如何避免在表单文件输入中没有提供任何内容时持久化该字段?使实体保持其旧值。当然,如果提供一个文件,我希望能删除旧的文件。
我的控制器看起来像这样:
if ($request->getMethod() == 'POST') {

    $form->bind($request);

    if ($form->isValid()) {

        $em->persist($team);
        $em->flush();
        ...
    }
}

我的实体的一部分涉及图像(我很确定我需要在这里做些什么,但不知道具体是什么):

/**
 * @ORM\PrePersist()
 * @ORM\PreUpdate()
 */
public function uploadImage() {
    // the file property can be empty if the field is not required
    if (null === $this->image) {
        return;
    }
    if(!$this->id){
        $this->image->move($this->getTmpUploadRootDir(), $this->image->getClientOriginalName());
    }else{
        $this->image->move($this->getUploadRootDir(), $this->image->getClientOriginalName());
    }
    $this->setImage($this->image->getClientOriginalName());
}

编辑

好的,我对这个答案的代码进行了一些更改,因为显然事件监听器在回调中要求一个FormEvent实例,而不是FormInterface实例。

$builder->addEventListener(FormEvents::POST_SUBMIT, function (FormEvent $event) {
// Retrieve submitted data
$form = $event->getForm();
$item = $event->getData();

// Test if upload image is null (maybe adapt it to work with your code)
if (null !== $form->get('image')->getData()) {
    var_dump($form->get('image')->getData());
    die('image provided');
    $item->setImage($form->get('image')->getData());
}

当我提供一个图片时,脚本会按预期进入测试并执行die()。但是如果我没有提供任何文件,则脚本不会进入if()测试,但是数据库中的字段仍然被清空为一个空值。有什么想法吗?

如下所要求,这是表格:

// src/Van/TeamsBundle/Form/TeamEditType.php

namespace Van\TeamsBundle\Form;

use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;

class TeamEditType extends TeamType // Ici, on hérite de ArticleType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        // On fait appel à la méthode buildForm du parent, qui va ajouter tous les champs à $builder
        parent::buildForm($builder, $options);
        // On supprime celui qu'on ne veut pas dans le formulaire de modification
        $builder->remove('image')
        ->add('image', 'file', array(
            'data_class' => null,
            'required' => false
        ))
        ;


        $builder->addEventListener(FormEvents::POST_SUBMIT, function (FormEvent $event) {
            // Retrieve submitted data
            $form = $event->getForm();
            $item = $event->getData();

            // Test if upload image is null (maybe adapt it to work with your code)
            if (null !== $form->get('image')->getData()) {
                var_dump($form->get('image')->getData());
                die('image provided');
                $item->setImage($form->get('image')->getData());
            }
        });


    }

    // On modifie cette méthode car les deux formulaires doivent avoir un nom différent
    public function getName()
    {
        return 'van_teamsbundle_teamedittype';
    }
}

以及整个团队实体:

<?php

namespace Van\TeamsBundle\Entity;

use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;

/**
 * Team
 *
 * @ORM\Table()
 * @ORM\HasLifecycleCallbacks
 * @ORM\Entity
 * @ORM\Entity(repositoryClass="Van\TeamsBundle\Entity\TeamRepository") @ORM\Table(name="van_teams")
 */
class Team
{
    /**
     * @var integer
     *
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    private $id;

    /**
     * @var string
     *
     * @ORM\Column(name="name", type="string", length=100)
     */
    private $name;

    /**
     * @var string
     *
     * @ORM\Column(name="countryCode", type="string", length=2)
     */
    private $countryCode;

    /**
     * @ORM\ManyToOne(targetEntity="Van\TeamsBundle\Entity\Game")
     * @ORM\JoinColumn(nullable=false)
     */
    private $game;

    /**
     * @ORM\ManyToOne(targetEntity="Van\TeamsBundle\Entity\Statut")
     * @ORM\JoinColumn(nullable=false)
     */
    private $statut;

    /**
     * @var string $image
     * @Assert\File( maxSize = "1024k", mimeTypesMessage = "Please upload a valid Image")
     * @ORM\Column(name="image", type="string", length=255)
     */
    private $image;



    /**
     * Get id
     *
     * @return integer 
     */
    public function getId()
    {
        return $this->id;
    }

    /**
     * Set name
     *
     * @param string $name
     * @return Team
     */
    public function setName($name)
    {
        $this->name = $name;

        return $this;
    }

    /**
     * Get name
     *
     * @return string 
     */
    public function getName()
    {
        return $this->name;
    }

    /**
     * Set countryCode
     *
     * @param string $countryCode
     * @return Team
     */
    public function setCountryCode($countryCode)
    {
        $this->countryCode = $countryCode;

        return $this;
    }

    /**
     * Get countryCode
     *
     * @return string 
     */
    public function getCountryCode()
    {
        return $this->countryCode;
    }

    /**
     * Set image
     *
     * @param string $image
     * @return Team
     */
    public function setImage($image)
    {
        $this->image = $image;

        return $this;
    }

    /**
     * Get image
     *
     * @return string 
     */
    public function getImage()
    {
        return $this->image;
    }

    /**
     * Set game
     *
     * @param \Van\TeamsBundle\Entity\Game $game
     * @return Team
     */
    public function setGame(\Van\TeamsBundle\Entity\Game $game)
    {
        $this->game = $game;

        return $this;
    }

    /**
     * Get game
     *
     * @return \Van\TeamsBundle\Entity\Game 
     */
    public function getGame()
    {
        return $this->game;
    }

    /**
     * Set statut
     *
     * @param \Van\TeamsBundle\Entity\Statut $statut
     * @return Team
     */
    public function setStatut(\Van\TeamsBundle\Entity\Statut $statut)
    {
        $this->statut = $statut;

        return $this;
    }

    /**
     * Get statut
     *
     * @return \Van\TeamsBundle\Entity\Statut 
     */
    public function getStatut()
    {
        return $this->statut;
    }






    public function getFullImagePath() {
        return null === $this->image ? null : $this->getUploadRootDir(). $this->image;
    }

    protected function getUploadRootDir() {
        // the absolute directory path where uploaded documents should be saved
        // return $this->getTmpUploadRootDir();
        return __DIR__ . '/../../../../web/uploads/';
    }

    protected function getTmpUploadRootDir() {
        // the absolute directory path where uploaded documents should be saved
        return __DIR__ . '/../../../../web/uploads_tmp/';
    }

    /**
     * @ORM\PrePersist()
     * @ORM\PreUpdate()
     */
    public function uploadImage() {
        // the file property can be empty if the field is not required
        if (null === $this->image) {
            return;
        }
        if(!$this->id){
            $this->image->move($this->getTmpUploadRootDir(), $this->image->getClientOriginalName());
        }else{
            $this->image->move($this->getUploadRootDir(), $this->image->getClientOriginalName());
        }
        $this->setImage($this->image->getClientOriginalName());
    }

    /**
     * @ORM\PostPersist()
     */
    public function moveImage()
    {
        if (null === $this->image) {
            return;
        }
        if(!is_dir($this->getUploadRootDir())){
            mkdir($this->getUploadRootDir());
        }
        copy($this->getTmpUploadRootDir().$this->image, $this->getFullImagePath());
        unlink($this->getTmpUploadRootDir().$this->image);
    }

    /**
     * @ORM\PreRemove()
     */
    public function removeImage()
    {
        unlink($this->getFullImagePath());
        rmdir($this->getUploadRootDir());
    }
}

编辑2

我已经做到了。当我提供一张图片时,它会被保存在数据库中的图片字段中,并重定向到我的主页。当我没有提供任何图片时,不会发生重定向,并且在我的表单中的文件输入框上方出现以下消息:“找不到该文件”。在我的TeamEditType类中,我做了以下操作,因此图片不是必需的。

$builder->remove('image')
->add('image', 'file', array(
    'data_class' => null,
    'required' => false
))
;
4个回答

18

从Symfony 2.3开始,你可以直接使用PATCH http方法,文档链接在这里

    $form = $this->createForm(FooType::class, $foo, array(
        'action' => $this->generateUrl('foo_update', array('id' => $foo->getId())),
        'method' => 'PATCH',
    ));

这是一种使用主表单进行实体部分更新的简便方法,而无需呈现所有字段。


太棒了。我认为这是最好的解决方案。 - kovalevsky

7

Symfony 2.4 中的一种方法(更多信息请参见 Symfony2 Cookbook):

public function buildForm(FormBuilderInterface $builder, array $options)
{
 // $builder->add() ... 

 $builder->addEventListener(FormEvents::POST_SUBMIT, function (FormInterface $form) {
        // Retrieve submitted data
        $form = $event->getForm();
        $image = $form->getData();

        // Test if upload image is null (maybe adapt it to work with your code)
        if (null !== $form->get('uploadImage')->getData()) {
            $image->setUploadImage($form->get('uploadImage')->getData());
        }
    });
}

编辑

看起来你已经测试了你的持久化数据。请尝试以下操作:

   public function setImage($image)
   {
        if($image !== null) {
            $this->image = $image;

            return $this;
        } 
    }

在我的原始帖子中添加了“编辑2”部分。你能看一下吗?我不明白为什么会出现这个消息。可能与实体ORM注释有关吗? - VaN
请查看食谱,了解他们如何处理文件。特别是在上传()方法中,在持久化实体之前。似乎对于文件,他们没有使用"require",我猜... - Debflav
我确实在食谱上花了很多时间,但是我无法弄清楚我的脚本出了什么问题...我做的事情与他们几乎一样。你说的“他们不使用require”是什么意思?你是指在构建器中设置字段时将“required”=> false吗? - VaN
是的...当你读取"file"时,它可以为空,但它们没有使用必填属性。 - Debflav
是的,但如果我不指定“required”=> false,则“required”会被隐式设置为true,当我提交空文件的表单时,表单不会提交并要求我填写此字段。 - VaN

0

我正在使用Symfony 3.3并遇到了相同的问题,以下是我克服它的解决方案。

您需要在实体中创建一个额外的属性,用于上传图像而不是图像属性,类似于 $file;

Product.php

/**
 * @var string
 *
 * @ORM\Column(name="image", type="string")
 *
 */
 private $image;

/**
 *
 * @Assert\File(mimeTypes={ "image/jpeg", "image/jpg", "image/png" })
 */
private $file;

public function setFile($file)
{
    $this->file = $file;
    return $this;
}
public function getFile()
{
    return $this->file;
}

// other code i.e image setter getter 
...

ProductType.php

   $builder->add('file', FileType::class, array(
            'data_class' => null,
            'required'=>false,
            'label' => 'Upload Image (jpg, jpeg, png file)')
   );

Form.html.twig

  <div class="form-group">
        {{ form_label(form.file) }}
        {{ form_widget(form.file, {'attr': {'class': 'form-control'}}) }}
    </div>

最后是ProductController.php

 ...
 if ($form->isSubmitted() && $form->isValid()) {
     $file = $item->getFile();
              if($file instanceof UploadedFile) {
                  $fileName = md5(uniqid()).'.'.$file->guessExtension();
                  $file->move(
                  $this->getParameter('upload_directory'),
                  $fileName
                  );
                  $item->setImage($fileName);
              }
  ...
  }
 ...

关于upload_directory的更多信息

app/config/config.html

 parameters:
    upload_directory: '%kernel.project_dir%/web/uploads'

创建一个名为uploads的目录在网站目录下。

0

我通过使用PHP反射API解决了这个问题。我的方法是浏览实体类的属性,并用已保存的值替换查询中未提供的值。

/**
 * @param \ReflectionClass $reflectionClass
 * @param $entity
 * @param Request $request
 * @return Request
 */
public function formatRequest($class, $entity, Request $request){

    $reflectionClass = new \ReflectionClass('AppBundle\Entity\\'.$class);

    foreach ($reflectionClass->getProperties() as $attribut)
    {
        $attribut->setAccessible(true); // to avoid fatal error when trying to access a non-public attribute

        if($request->request->get($attribut->getName()) == null) { // if the attribute value is not provided in the request
            $request->request->set($attribut->getName(), $attribut->getValue($entity)); 
        }
    }

    return $request;
}

然后我像这样使用它:

$request = $this->formatRequest("EntityName", $entity, $request);

这真的很泛型。


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