如何在Symfony2中处理具有多对一关系的文件上传


How to handle a file upload with a many to one relationship in Symfony2?

我正在创建一个用于练习的票证系统。

现在我在上传文件时遇到问题。

这个想法是一个票证可以有多个附件,所以我在票证和上传表之间创建了一个多对一的关系。

class Ticket {
// snip
/**
 * @ORM'OneToMany(targetEntity="Ticket", mappedBy="ticket")
 */
protected $uploads;
// snip
}

上传实体类包含上传功能,这是我从本教程中获得的:

<?php
namespace Sytzeandreae'TicketsBundle'Entity;
use Doctrine'ORM'Mapping as ORM;
use Symfony'Component'Validator'Constraints as Assert;
use Symfony'Component'HttpFoundation'File'UploadedFile;
/**
 * @ORM'Entity(repositoryClass="Sytzeandreae'TicketsBundle'Repository'UploadRepository")
 * @ORM'Table(name="upload")
 * @ORM'HasLifecycleCallbacks
 */
class Upload
{
    /**
     * @Assert'File(maxSize="6000000")
     */
    private $file;
    private $temp;
    /**
     * @ORM'Id
     * @ORM'Column(type="integer")
     * @ORM'GeneratedValue(strategy="AUTO")
     */
    protected $id;
    /**
     * @ORM'ManyToOne(targetEntity="Ticket", inversedBy="upload")
     * @ORM'JoinColumn(name="ticket_id", referencedColumnName="id")
     */
    protected $ticket;
    /**
     * @ORM'Column(type="string")
     */
    protected $title;
    /**
     * @ORM'Column(type="string")
     */
    protected $src; 
    public function getAbsolutePath()
    {
        return null === $this->src
            ? null
            : $this->getUploadRootDir().'/'.$this->src;
    }
    public function getWebPath()
    {
        return null === $this->src
            ? null
            : $this->getUploadDir().'/'.$this->src;
    }
    public function getUploadRootDir()
    {
        // The absolute directory path where uplaoded documents should be saved
        return __DIR__.'/../../../../web/'.$this->getUploadDir();
    }
    public function getUploadDir()
    {
        // Get rid of the __DIR__ so it doesn/t screw up when displaying uploaded doc/img in the view
        return 'uploads/documents';
    }
    /**
     * Sets file
     * 
     * @param UploadedFile $file
     */
    public function setFile(UploadedFile $file = null)
    {
        $this->file = $file;
        if (isset($this->path)) {
            // store the old name to delete after the update
            $this->temp = $this->path;
            $this->path = null;
        } else {
            $this->path = 'initial';
        }
    }
    /**
     * Get file
     *
     * @return UploadedFile
     */
    public function getFile()
    {
        return $this->file;
    }
    /**
     * Get id
     *
     * @return integer 
     */
    public function getId()
    {
        return $this->id;
    }
    /**
     * Set title
     *
     * @param string $title
     */
    public function setTitle($title)
    {
        $this->title = $title;
    }
    /**
     * Get title
     *
     * @return string 
     */
    public function getTitle()
    {
        return $this->title;
    }
    /**
     * Set src
     *
     * @param string $src
     */
    public function setSrc($src)
    {
        $this->src = $src;
    }
    /**
     * Get src
     *
     * @return string 
     */
    public function getSrc()
    {
        return $this->src;
    }
    /**
     * Set ticket
     *
     * @param Sytzeandreae'TicketsBundle'Entity'Ticket $ticket
     */
    public function setTicket('Sytzeandreae'TicketsBundle'Entity'Ticket $ticket)
    {
        $this->ticket = $ticket;
    }
    /**
     * Get ticket
     *
     * @return Sytzeandreae'TicketsBundle'Entity'Ticket 
     */
    public function getTicket()
    {
        return $this->ticket;
    }
    /**
     * @ORM'PrePersist()
     * @ORM'PreUpdate()
     */
    public function preUpload()
    {
        if (null !== $this->getFile()) {
            // do whatever you want to generate a unique name
            $filename = sha1(uniqid(mt_rand(), true));
            $this->src = $filename.'.'.$this->getFile()->guessExtension();
        }
    }
    public function upload()
    {
        // the file property can be empty if the field is not required
        if (null === $this->getFile()) {
            return;
        }
        // move takes the target directory and then the target filename to move to
        $this->getFile()->move(
            $this->getUploadRootDir(),
            $this->src
        );
        // check if we have an old image
        if (isset($this->temp)) {
            // delete the old image
            unlink($this->getUploadRootDir().'/'.$this->temp);
            // clear the temp image path
            $this->temp = null;
        }
        // clean up the file property as you won't need it anymore
        $this->file = null;
    }
    /**
     * @ORM'PostRemove
     */
    public function removeUpload()
    {
        if ($file = $this->getAbsolutePath()) {
            unlink($file);
        }   
    }
}

表单构建如下:

class TicketType extends AbstractType
{
    public function buildForm(FormBuilder $builder, array $options)
    {
        $builder
            ->add('title')
            ->add('description')
            ->add('priority')
            ->add('uploads', new UploadType())
    }

UploadType如下所示:

class UploadType extends AbstractType
{
    public function buildForm(FormBuilder $builder, array $options) {
        $builder->add('file', 'file', array(
            'label' => 'Attachments',
            'required' => FALSE,
            'attr' => array (
                'accept' => 'image/*',
                'multiple' => 'multiple'
            )
        ));
    }

这部分似乎工作得很好,我确实收到了一个包含文件上传器的表格。

一旦我把这一行放在Ticket实体的构造函数中:$this->uploads=new''Doctrine''Common''Collections''ArrayCollection();它给我带来了以下错误:

属性"file"、方法"getFile()"和方法"isFile()存在于类"Doctrine''Common''Collections''ArrayCollection"中

如果我省略了这一行,并上传了一个文件,它会给我带来以下错误:

"Sytzeandreae''TicketsBundle''Entity''Ticket"。也许你应该创造方法"setUploads()"?

所以我做的下一件事是创建这个方法,再次尝试上传,现在它抛出了我:

类Symfony''Component''HttpFoundation''File''UploadedFile不是有效实体或映射的超类。

这是我真正陷入困境的地方。我不知道在什么阶段,我做错了什么,我希望得到一些帮助:)

谢谢!

您可以将集合字段类型UploadType()添加到表单中。请注意,您不能将multiple选项用于当前的Upload实体。。。但这是最快的解决方案。

或者调整您的Ticket实体,使其能够处理ArrayCollection中的多个文件并在这些文件上循环。