Symfony2表单字段在发生验证错误时没有更新

时间:2022-10-22 18:17:50

Here is my form type:

以下是我的表单类型:

class TestFormType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {

        $builder->add('thumbnail', 'hidden', array(
            'label' => 'Thumbnail',
            'label_attr' => array(
                'class' => 'col-xs-2 control-label'
            ),
            'required' => false,
            'error_bubbling' => true,
            'required' => false
        ));

        $builder->add('thumbnail_data', 'file', array(
            'error_bubbling' => true,
            'required' => false
        ));
    }

    public function setDefaultOptions(\Symfony\Component\OptionsResolver\OptionsResolverInterface $resolver)
    {
        $resolver->setDefaults(array(
            'data_class' => 'X\LibraryBundle\Entity\Test',
            'cascade_validation' => true,
            'error_bubbling' => true,
        ));

    }

    public function getName()
    {
        return 'test';
    }
}

Here is the entity, important part is the setThumbnailData($file) method, which stores the thumbnail file and sets the thumbnail path via the setThumbnail(string) method.

这里是实体,重要的部分是setThumbnailData($file)方法,它存储缩略图文件,并通过setThumbnail(string)方法设置缩略图路径。

<?php

namespace X\LibraryBundle\Entity;

use Doctrine\ORM\Mapping as ORM;

/**
 * Test
 *
 * @ORM\Table(name="test")
 * @ORM\Entity(repositoryClass="X\LibraryBundle\Repository\TestRepository")
 */
class Test
{
    /**
     * @ORM\Column(name="id", type="integer", nullable=false)
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    protected $id;

    /**
     * @ORM\Column(type="text", nullable=true)
     */
    protected $thumbnail;

    /**
     * Set thumbnail
     *
     * @param string $thumbnail
     * @return Test
     */
    public function setThumbnail($thumbnail)
    {
        $this->thumbnail = $thumbnail;

        return $this;
    }

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

    /**
     * This will save file to disk
     * @param $file
     */
    public function setThumbnailData($file) {
        if($file !== null && $file !== false)
        {
            $fileName = $file->getClientOriginalName();
            $baseDir = __DIR__ . '/../../../../../../../web/uploads/promotional_material/';
            $dir =  sha1(microtime());
            while (is_dir($baseDir . $dir)) {
                $dir = sha1(microtime());
            }
            mkdir($baseDir . $dir);
            $this->setThumbnail('/uploads/promotional_material/' . $dir . '/' . $fileName);
            $file->move($baseDir . $dir, $fileName);
        }
    }

    public function getThumbnailData() {
        return '';
    }
}

Now the issue is, if the form runs into a validation error, the following twig lines produce different output, the correct value outputted from the second line, the other produces the original thumbnail path. So if I output the thumbnail input using {{ form_widget(form.thumbnail) }}, I get the original thumbnail path, not the one that should have changed via the setThumbnailData() method.

现在的问题是,如果表单遇到验证错误,下面的分支线将产生不同的输出,正确的值将从第二行输出,另一个生成原始的缩略图路径。因此,如果我使用{{{{form_widget(form.thumbnail)}输出缩略图输入,我将得到原始的缩略图路径,而不是应该通过setThumbnailData()方法进行更改的缩略图路径。

{{ dump(form.thumbnail.vars.data) }}
{{ dump(form.vars.data.thumbnail) }}

Is the issue caused by setting the thumbnail using the setThumbnailData() method? Not sure how to fix this other than hard coding the input in twig like so:

使用setThumbnailData()方法设置缩略图会导致问题吗?不确定如何解决这个问题,而不是硬编码在twig中的输入:

<input type="hidden" name="test[thumbnail]" value="{{ form.vars.value.thumbnail }}"/>

2 个解决方案

#1


5  

I cannot exactly solve your problem, because in my opinion you're doing it the wrong way. A data class is actually only responsibly for keeping your data, so your method set/getThumbnailData should look like this

我不能完全解决你的问题,因为我认为你做错了。数据类实际上只负责保存数据,因此您的方法集/getThumbnailData应该如下所示

<?php
// ...
private $thumbnailData;

public function setThumbnailData(UploadedFile $thumbnailData) {
    $this->thumbnailData = $thumbnailData;
}

public function getThumbnailData() {
    return $this->thumbnailData;
}

In your controller you have something like this:

在你的控制器中你有这样的东西:

<?php
// ...
public function formAction() {

    $form = // ... 
    $form->handleRequest($request);
    if($form->isValid()) {
        $test = $form->getData();
        /* @var $test Test */
        $thumbnailUploader = $this->get('thumbnail_uploader');
        /* @var $thumbnailUploader ThumbnailUploadService */

        $file = $test->getThumbnailData();
        $filename = $file->getClientOriginalName();
        $thumbnailUploader->upload($filename, $file);

        // ...
    }
}

I recommend to move all logic which has nothing to do with forms or requests into services. Your service in this case can be more generic handle any symfony files with a given filename.

我建议将与表单或请求无关的所有逻辑移到服务中。在这种情况下,您的服务可以更通用地处理任何带有给定文件名的symfony文件。

<?php
class ThumbnailUploadService {
    /**
     * @param $file Symfony\Component\HttpFoundation\File\File
     *
     */
    public function upload($filename, File $file) {
        // ...
    }
}

#2


0  

Also there is another way using callbacks. Here i give you a generic class to uploads image and generate the thumbnails. I hope this work for you. This class manage the files generated automatically when you create update o delete the entity.

还有一种使用回调的方法。这里我给您一个通用类来上传图像并生成缩略图。我希望这对你有用。这个类管理在创建update o delete实体时自动生成的文件。

use Doctrine\ORM\Mapping as ORM;
use Symfony\Bridge\Doctrine\Validator\Constraints as DoctrineAssert;
use Symfony\Component\HttpFoundation\File\UploadedFile;
use Symfony\Component\Validator\Constraints as Assert;
use App\MyBundleBundle\Util\Util;

/**
 * Image
 *
 * @ORM\Table(name="image")
 * @ORM\Entity()
 * @ORM\HasLifecycleCallbacks
 */
class Image
{

    /**
     * @var integer
     *
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     *
     */
    private $id;

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


    private $temp;

    /**
     * @ORM\Column(type="string", length=255)
     */
    protected $path;

    /**
     * @Assert\Image(maxSize="5M")
     */
    private $file;

    /**
     * Sets file.
     *
     * @param UploadedFile $file
     */
    public function setFile(UploadedFile $file = null)
    {
        $this->file= $file;

        if (isset($this->path)) {
            $this->temp = $this->path;
            $this->path= null;
        } else {
            $this->path= 'initial';
        }
    }

    /**
     * Get file.
     *
     * @return UploadedFile
     */
    public function getFile()
    {
        return $this->file;
    }

    /**
     * @ORM\PrePersist()
     * @ORM\PreUpdate()
     */
    public function preUpload()
    {
        if (null !== $this->getFile()) {
            // do whatever you want to generate a unique name
            $filename = Util::getSlug($this->name) . uniqid() . '.' . $this->file->guessExtension();
            $this->path = $filename;
        }
    }

    /**
     * @ORM\PostPersist()
     * @ORM\PostUpdate()
     */
    public function upload()
    {
        if (null === $this->getFile()) {
            return;
        }

        $this->getFile()->move($this->getUploadRootDir(), $this->path);
        if (isset($this->temp)) {
            if (file_exists($this->getUploadRootDir() .'/'. $this->temp)) {
                unlink($this->getUploadRootDir() . '/' . $this->temp);
            }
            $this->temp = null;
        }
        $this->file= null;
        //create a dir to save de thumbnails
        if (!file_exists($this->getUploadRootDir() . '/thumbnails')) {
            mkdir($this->getUploadRootDir() . '/thumbnails');
        }

        //call a method in util class to generate the thumbnails
        Util::redim($this->getUploadRootDir() . '/' . $this->path, $this->getUploadRootDir() . '/thumbnails/' . $this->path, 128, 128);
    }

    /**
     * @ORM\PostRemove()
     */
    public function removeUpload()
    {
        //This is to remove the files when the entity is delete
        if ($file = $this->getAbsolutePath()) {
            if (file_exists($file)) {
                unlink($file);
                $thumb = $this->getUploadRootDir() . '/thumbnails/' . $this->getPath();
                if (file_exists($thumb)) {
                    unlink($thumb);
                }
            }
        }
    }

    public function getAbsolutePath()
    {
        return null === $this->path ? null : $this->getUploadRootDir() . '/' . $this->path;
    }

    public function getWebPath()
    {
        return null === $this->path? null :
            $this->getUploadDir() . '/' . $this->path;
    }

    protected function getUploadRootDir()
    {
        return $this->getUploadDir();
    }

    protected function getUploadDir()
    {
        return 'uploads/image/';
    }

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

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

        return $this;
    }

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


    /**
     * Set path
     *
     * @param string $path
     * @return Imagen
     */
    public function setPath($path)
    {
        $this->path= $path;

        return $this;
    }

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

    public function __toString()
    {
        return $this->getName();
    }
}

#1


5  

I cannot exactly solve your problem, because in my opinion you're doing it the wrong way. A data class is actually only responsibly for keeping your data, so your method set/getThumbnailData should look like this

我不能完全解决你的问题,因为我认为你做错了。数据类实际上只负责保存数据,因此您的方法集/getThumbnailData应该如下所示

<?php
// ...
private $thumbnailData;

public function setThumbnailData(UploadedFile $thumbnailData) {
    $this->thumbnailData = $thumbnailData;
}

public function getThumbnailData() {
    return $this->thumbnailData;
}

In your controller you have something like this:

在你的控制器中你有这样的东西:

<?php
// ...
public function formAction() {

    $form = // ... 
    $form->handleRequest($request);
    if($form->isValid()) {
        $test = $form->getData();
        /* @var $test Test */
        $thumbnailUploader = $this->get('thumbnail_uploader');
        /* @var $thumbnailUploader ThumbnailUploadService */

        $file = $test->getThumbnailData();
        $filename = $file->getClientOriginalName();
        $thumbnailUploader->upload($filename, $file);

        // ...
    }
}

I recommend to move all logic which has nothing to do with forms or requests into services. Your service in this case can be more generic handle any symfony files with a given filename.

我建议将与表单或请求无关的所有逻辑移到服务中。在这种情况下,您的服务可以更通用地处理任何带有给定文件名的symfony文件。

<?php
class ThumbnailUploadService {
    /**
     * @param $file Symfony\Component\HttpFoundation\File\File
     *
     */
    public function upload($filename, File $file) {
        // ...
    }
}

#2


0  

Also there is another way using callbacks. Here i give you a generic class to uploads image and generate the thumbnails. I hope this work for you. This class manage the files generated automatically when you create update o delete the entity.

还有一种使用回调的方法。这里我给您一个通用类来上传图像并生成缩略图。我希望这对你有用。这个类管理在创建update o delete实体时自动生成的文件。

use Doctrine\ORM\Mapping as ORM;
use Symfony\Bridge\Doctrine\Validator\Constraints as DoctrineAssert;
use Symfony\Component\HttpFoundation\File\UploadedFile;
use Symfony\Component\Validator\Constraints as Assert;
use App\MyBundleBundle\Util\Util;

/**
 * Image
 *
 * @ORM\Table(name="image")
 * @ORM\Entity()
 * @ORM\HasLifecycleCallbacks
 */
class Image
{

    /**
     * @var integer
     *
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     *
     */
    private $id;

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


    private $temp;

    /**
     * @ORM\Column(type="string", length=255)
     */
    protected $path;

    /**
     * @Assert\Image(maxSize="5M")
     */
    private $file;

    /**
     * Sets file.
     *
     * @param UploadedFile $file
     */
    public function setFile(UploadedFile $file = null)
    {
        $this->file= $file;

        if (isset($this->path)) {
            $this->temp = $this->path;
            $this->path= null;
        } else {
            $this->path= 'initial';
        }
    }

    /**
     * Get file.
     *
     * @return UploadedFile
     */
    public function getFile()
    {
        return $this->file;
    }

    /**
     * @ORM\PrePersist()
     * @ORM\PreUpdate()
     */
    public function preUpload()
    {
        if (null !== $this->getFile()) {
            // do whatever you want to generate a unique name
            $filename = Util::getSlug($this->name) . uniqid() . '.' . $this->file->guessExtension();
            $this->path = $filename;
        }
    }

    /**
     * @ORM\PostPersist()
     * @ORM\PostUpdate()
     */
    public function upload()
    {
        if (null === $this->getFile()) {
            return;
        }

        $this->getFile()->move($this->getUploadRootDir(), $this->path);
        if (isset($this->temp)) {
            if (file_exists($this->getUploadRootDir() .'/'. $this->temp)) {
                unlink($this->getUploadRootDir() . '/' . $this->temp);
            }
            $this->temp = null;
        }
        $this->file= null;
        //create a dir to save de thumbnails
        if (!file_exists($this->getUploadRootDir() . '/thumbnails')) {
            mkdir($this->getUploadRootDir() . '/thumbnails');
        }

        //call a method in util class to generate the thumbnails
        Util::redim($this->getUploadRootDir() . '/' . $this->path, $this->getUploadRootDir() . '/thumbnails/' . $this->path, 128, 128);
    }

    /**
     * @ORM\PostRemove()
     */
    public function removeUpload()
    {
        //This is to remove the files when the entity is delete
        if ($file = $this->getAbsolutePath()) {
            if (file_exists($file)) {
                unlink($file);
                $thumb = $this->getUploadRootDir() . '/thumbnails/' . $this->getPath();
                if (file_exists($thumb)) {
                    unlink($thumb);
                }
            }
        }
    }

    public function getAbsolutePath()
    {
        return null === $this->path ? null : $this->getUploadRootDir() . '/' . $this->path;
    }

    public function getWebPath()
    {
        return null === $this->path? null :
            $this->getUploadDir() . '/' . $this->path;
    }

    protected function getUploadRootDir()
    {
        return $this->getUploadDir();
    }

    protected function getUploadDir()
    {
        return 'uploads/image/';
    }

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

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

        return $this;
    }

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


    /**
     * Set path
     *
     * @param string $path
     * @return Imagen
     */
    public function setPath($path)
    {
        $this->path= $path;

        return $this;
    }

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

    public function __toString()
    {
        return $this->getName();
    }
}