Symfony2:如果未提供,请不要更新表单字段

时间:2014-03-21 11:18:46

标签: symfony doctrine-orm persistence

我的“团队”实体有一个表单。该实体具有“图像”字段。此字段在创建过程中是必需的,但在编辑过程中不需要。但是现在,在编辑过程中,如果我没有在文件输入中提供任何图像,则空输入仍然存在,因此我的数据库字段在此过程中被清空。 如果表单文件输入中没有提供任何内容,我该怎么做才能避免此字段的持久性?因此,实体保留此字段的旧值。当然,如果提供了一个文件,我希望他删除旧文件。

我的控制器看起来像这样:

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());
}

修改

好的,我对this answer's code代码进行了一些更改,因为显然事件侦听器在其回调中要求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 个答案:

答案 0 :(得分:14)

从Symfony 2.3开始,您只需使用 PATCH http方法,如文档there所述。

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

这是一种使用主窗体部分更新实体的简单方法,无需渲染所有字段。

答案 1 :(得分:6)

Symfony 2.4中的一种方法(Symfony2 coockbook中的更多信息):

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());
        }
    });
}

修改

您似乎已经在测试了prepersit数据。请尝试以下方法:

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

            return $this;
        } 
    }

答案 2 :(得分: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

的更多信息

应用程序/配置/ config.html

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

在web目录中创建一个uploads目录。

答案 3 :(得分:0)

我通过使用PHP Reflection 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);

这真的很通用。