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

时间:2013-07-16 12:27:53

标签: php symfony file-upload doctrine-orm many-to-one

我正在为练习目的创建一个票务系统。

目前我在上传文件方面遇到了问题。

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

class Ticket {

// snip

/**
 * @ORM\OneToMany(targetEntity="Ticket", mappedBy="ticket")
 */
protected $uploads;

// snip
}

上传实体类包含我从this教程中获取的上传功能:

<?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-&gt; uploads = new \ Doctrine \ Common \ Collections \ ArrayCollection(); 它抛出了以下错误:

  

属性“file”和方法“getFile()”以及方法“isFile()”都没有   存在于“Doctrine \ Common \ Collections \ ArrayCollection”类中

如果我将此行保留,并上传文件,则会引发以下错误:

  

“Sytzeandreae \ TicketsBundle \实体\票”。也许你应该创造   方法“setUploads()”?

接下来我做的就是创建这个方法,再次尝试上传,现在它抛出了我:

  

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

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

谢谢!

1 个答案:

答案 0 :(得分:0)

您可以在表单中添加collection field-type UploadType() Upload。请注意,您不能对当前的Ticket实体使用multiple选项...但这是最快的解决方案。

或者使{{1}}实体适应能够处理ArrayCollection中的多个文件并循环遍历它们。