Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

how to "manually" process file upload with symfony2?

Because I have a custom built jQuery plugin to pass file uploads to my symfony2 webapp I am looking for ways to handle this upload in the controller.

The standard (non-ajax) file upload that I currently have (and that works fine for synchronous calls) looks like this

Controller excerpt

    ...

    $entity  = new Image();
    $request = $this->getRequest();
    $form    = $this->createForm(new ImageType($createAction), $entity);
    $form->bind($request); // <-- Find a way to make this connection manually?!

    //check that a file was chosen
    $fileExists = isset($entity->file);

    if ( ($form->isValid()) && ($fileExists) ) {

        $em = $this->getDoctrine()->getManager();

        $em->persist($entity);
        $em->flush();

    }

    ...

Form Type: The form just takes the file and a name:

class ImageType extends AbstractType
{

    ...

    public function buildForm(FormBuilderInterface $builder, array $options)
    {

        $createAction = $this->createAction;

        if ($createAction) {        
            $builder
                ->add('file')
            ;
        }

        $builder
            ->add('name', 'text', array('label' => 'Namn'))
        ;
    }

    ...

}

As I understand (or in other words DON'T apparently understand) the file upload system with symfony2 and doctrine there is quite a bit of magic going on underneath the hood on this call

$form->bind($request);

For example, if I skip this bind() and instead try to create the Image entity manually like this...

    $request = $this->getRequest();
    $parent = $request->request->get('parent');
    $file = $request->request->get('file1');
    $name = $request->request->get('name');

    $entity->setName( $name );
    $entity->setFile( $file );
    $entity->setFolder( null );

... I find that it doesn't even have a setFile() so that is taken care of in some other way. Here is that Image entity:

namespace BizTV\MediaManagementBundle\Entity;

use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;

/**
 * BizTV\MediaManagementBundle\Entity\Image
 *
 * @ORM\Table(name="image")
 * @ORM\Entity
 * @ORM\HasLifecycleCallbacks
 */
class Image
{
    /**
     * @var integer $id
     *
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    private $id;

    /**
     * @var string $name
     *
     * @ORM\Column(name="name", type="string", length=255)
     * @Assert\NotBlank(message = "Bilden måste ha ett namn")
     */
    private $name;

    /**
     * @var integer $width
     *
     * @ORM\Column(name="width", type="integer")
     */
    private $width;

    /**
     * @var integer $height
     *
     * @ORM\Column(name="height", type="integer")
     */
    private $height;

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


//The deleteRequested variable is to flag that an image has been deleted by user.
//Due to slideshow issues we can however not delete the image right away, we can't risk to remove it from the
//cache manifest before the slideshow round is up.

    /**
     * @var time $deleteRequested
     * 
     * @ORM\Column(name="delete_requested", type="datetime", nullable=true)
     */
    private $deleteRequested;


    /**
    * @var object BizTV\BackendBundle\Entity\company
    *  
    * @ORM\ManyToOne(targetEntity="BizTV\BackendBundle\Entity\company")
    * @ORM\JoinColumn(name="company", referencedColumnName="id", nullable=false)
    */
    protected $company;     

    /**
    * @var object BizTV\MediaManagementBundle\Entity\Folder
    *  
    * @ORM\ManyToOne(targetEntity="BizTV\MediaManagementBundle\Entity\Folder")
    * @ORM\JoinColumn(name="folder", referencedColumnName="id", nullable=true)
    */
    protected $folder;


    /**
     * @Assert\File(maxSize="12000000")
     */
     public $file;

    /**
     * @ORM\OneToOne(targetEntity="BizTV\MediaManagementBundle\Entity\QrImage", mappedBy="image")
     */
    protected $qr;

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

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

        // if there is an error when moving the file, an exception will
        // be automatically thrown by move(). This will properly prevent
        // the entity from being persisted to the database on error
        $this->file->move($this->getUploadRootDir(), $this->path);

        unset($this->file);
    }

    /**
     * @ORM\PostRemove()
     */
    public function removeUpload()
    {
        if ($file = $this->getAbsolutePath()) {
            unlink($file);
        }
    }    

    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()
    {
        // the absolute directory path where uploaded documents should be saved
        return __DIR__.'/../../../../web/'.$this->getUploadDir();

    }

    protected function getUploadDir()
    {
        // get rid of the __DIR__ so it doesn't screw when displaying uploaded doc/image in the view.
        return 'uploads/images/'.$this->getCompany()->getId();
    }


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

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

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

    /**
     * Set width
     *
     * @param integer $width
     */
    public function setWidth($width)
    {
        $this->width = $width;
    }

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

    /**
     * Set height
     *
     * @param integer $height
     */
    public function setHeight($height)
    {
        $this->height = $height;
    }

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

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

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

    /**
     * Set company
     *
     * @param BizTV\BackendBundle\Entity\company $company
     */
    public function setCompany(\BizTV\BackendBundle\Entity\company $company)
    {
        $this->company = $company;
    }

    /**
     * Get company
     *
     * @return BizTV\BackendBundle\Entity\company 
     */
    public function getCompany()
    {
        return $this->company;
    }

    /**
     * Set folder
     *
     * @param BizTV\MediaManagementBundle\Entity\Folder $folder
     */
    public function setFolder(\BizTV\MediaManagementBundle\Entity\Folder $folder = NULL)
    {
        $this->folder = $folder;
    }

    /**
     * Get folder
     *
     * @return BizTV\MediaManagementBundle\Entity\Folder 
     */
    public function getFolder()
    {
        return $this->folder;
    }


    /**
     * Set qr
     *
     * @param BizTV\MediaManagementBundle\Entity\QrImage $qr
     */
    public function setQr(\BizTV\MediaManagementBundle\Entity\QrImage $qr = null)
    {
        $this->qr = $qr;
    }

    /**
     * Get qr
     *
     * @return BizTV\MediaManagementBundle\Entity\QrImage 
     */
    public function getQr()
    {
        return $this->qr;
    }


    /**
     * Set deleteRequested
     *
     * @param date $deleteRequested
     */
    public function setDeleteRequested($deleteRequested = null)
    {
        $this->deleteRequested = $deleteRequested;
    }

    /**
     * Get deleteRequested
     *
     * @return date 
     */
    public function getDeleteRequested()
    {
        return $this->deleteRequested;
    }
}
like image 813
Matt Welander Avatar asked Nov 06 '15 12:11

Matt Welander


3 Answers

I found what I was looking for. To access the files uploaded to symfony from the controller, you just need to do this:

    $request = $this->getRequest();
    $file = $request->files->get('file1'); //file1 being the name of my form field for the file

    /* if your entity is set up like mine - like they teach you in the symfony2 cookbook
     * file is actually a public property so you can just set it like this
     **/
    $entity->file = $file; 

    //and here's how you get the original name of that file
    $entity->setName( $file->getClientOriginalName() );
like image 67
Matt Welander Avatar answered Oct 07 '22 20:10

Matt Welander


First of all, if you want to get an entity with your file after form submit/bind/handleRequest or smth else, you need to provide data_class option in form configuration method (setDefaultOptions etc.). And only after that your form will start to return needed entity after submission.

like image 38
Ilya Yarkovets Avatar answered Oct 07 '22 19:10

Ilya Yarkovets


1)First of All create your entity :

namespace XXX;

use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;

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

      /**
     * @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 image
     *
     * @param string $image
     */
    public function setImage($image)
    {
        $this->image = $image;
    }

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

}

2) build your form: So we will then create a simple form type for this Article entity in order to fit into the other forms: ArticleType.php

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilder;

class ArticleType extends AbstractType
{
    public function buildForm(FormBuilder $builder, array $options)
    {
        $builder
            ->add('image')
            ->add('...')

        ;
    }

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

3)Create the controller: The controller below shows you how to manage the whole process: ArticleController.php:

use Symfony\Bundle\FrameworkBundle\Controller\Controller;

/**
 * Article controller.
 *
 */
class ArticleController extends Controller
{

    /**
     * Finds and displays a Article entity.
     *
     */
    public function showAction($id)
    {

        $em = $this->getDoctrine()->getEntityManager();
        $entity = $em->getRepository('XXXBundle:Article')->find($id);
        if (!$entity) {
            throw $this->createNotFoundException('Unable to find Article entity.');
        }
    return $this->render('XXXBundle:Article:show.html.twig', array(
            'entity'      => $entity,

        ));
    }
    /**
     * Displays a form to create a new Article entity.
     *
     */
    public function newAction()
    {
        $entity = new Article();
        //$entity = $em->getRepository('CliniqueGynecoBundle:Article');
        $form   = $this->createForm(new ArticleType(), $entity);

        return $this->render('XXXBundle:Article:new.html.twig', array(
            'entity' => $entity,
            'form'   => $form->createView()
        ));
    }

    /**
     * Creates a new Article entity.
     *
     */
    public function createAction()
    {
        $entity  = new Article();
        $request = $this->getRequest();
        $form    = $this->createForm(new ArticleType(), $entity);
        $form->bindRequest($request);

        if ($form->isValid()) {
            $em = $this->getDoctrine()->getEntityManager();
            $em->persist($entity);
            $em->flush();

            return $this->redirect($this->generateUrl('article_show', array('id' => $entity->getId())));

        }

        return $this->render('XXXBundle:Article:new.html.twig', array(
            'entity' => $entity,
            'form'   => $form->createView()
        ));
    }

    private function createDeleteForm($id)
    {
        return $this->createFormBuilder(array('id' => $id))
            ->add('id', 'hidden')
            ->getForm()
        ;
    }
}

4) layout for the upload form: new.html.twig

<form action="{{ path('basearticle_create') }}" method="post" {{ form_enctype(form) }}>
    {{ form_widget(form) }}
    <p>
        <button class="btn btn-primary" type="submit">Create</button>
    </p>
</form>

5) Display layout: show.html.twig

<table>
 <tr>
      <td align="center" valign="top"><img src="{{ asset('upload/' ~ entity.id ~'/' ~ entity.image)}}" alt="" height="525" width="666" /></td>
</tr>
</table>

6) Use the « Lifecycle Callbacks » hooking the entity in a « Lifecycle callbacks »: « @ ORM \ HasLifecycleCallbacks »

/**
 *
 * @ORM\Table()
 * @ORM\HasLifecycleCallbacks
 * @ORM\Entity
 */
class Article
{
....

7) Added methods to download files:

class Article
{
   ....................................

    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().$this->getId()."/";
    }

    protected function getTmpUploadRootDir() {
        // the absolute directory path where uploaded documents should be saved
        return __DIR__ . '/../../../../web/upload/';
    }

    /**
     * @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());
    }
like image 21
youngdero Avatar answered Oct 07 '22 21:10

youngdero