Как обрабатывать загрузку файла с отношением «многие к одному» в 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(); Это выдает мне следующую ошибку:

Ни свойство "файл", ни метод "getFile()", ни метод "isFile()" не существуют в классе "Doctrine\Common\Collections\ArrayCollection"

Если я оставлю эту строку и загружу файл, он выдаст мне следующую ошибку:

"Sytzeandreae\TicketsBundle\Entity\Ticket". Может быть, вам стоит создать метод "setUploads()"?

Итак, следующее, что я сделал, это создал этот метод, повторил попытку загрузки, и теперь он выдает меня:

Класс Symfony\Component\HttpFoundation\File\UploadedFile не является допустимым объектом или сопоставленным суперклассом.

Вот где я действительно застрял. Я не вижу, что, на каком этапе я сделал неправильно, и надеюсь на помощь :)

Спасибо!


person Njerpie    schedule 16.07.2013    source источник
comment
Вы пробовали это решение?   -  person ferdynator    schedule 16.07.2013
comment
Да, я сделал, хотя не уверен, что я сделал это правильно. Он все еще выдавал мне ошибку UploadedFile не является допустимой ошибкой объекта...   -  person Njerpie    schedule 16.07.2013
comment
как выглядит ваш метод setUpload?   -  person ferdynator    schedule 16.07.2013
comment
/** * Установить загрузки * * @param Sytzeandreae\TicketsBundle\Enitity\Upload $uploads */ public function setUploads($uploads) { $this-›uploads = $uploads; }   -  person Njerpie    schedule 16.07.2013
comment
Подскажите, пожалуйста, какое решение этой проблемы?   -  person Adib Aroui    schedule 24.01.2014
comment
На самом деле я еще не смог это исправить. Я застрял с одной загрузкой :(   -  person Njerpie    schedule 24.01.2014


Ответы (1)


Вы можете либо добавить тип поля коллекции UploadType() в свой форма. Обратите внимание, что вы не можете использовать множественный вариант с текущим объектом Upload... но это самое быстрое решение.

Или адаптируйте свой объект Ticket, чтобы он мог обрабатывать несколько файлов в коллекции ArrayCollection и зацикливаться на них.

person Nicolai Fröhlich    schedule 16.07.2013