Doctrine File Upload processing example

Source: Internet
Author: User
Tags assert event listener file upload flush

Basic settings


To create a simple doctrine entity class:


 src/acme/demobundle/entity/document.php namespace acme\demobundle\entity;
  use doctrine\orm\mapping as orm;
use symfony\component\validator\constraints as assert;  /**  *  @ORM \entity  */class document {    /**       *  @ORM \id      *  @ORM \column (type= "integer")       *  @ORM \generatedvalue (strategy= "AUTO")      */   
 public  $id;       /**      *  @ORM \column (type= "string",  length= 255)      *  @Assert \notblank      */   
 public  $name;       /**      *  @ORM \column (type= "string",  length= 255, nullable=true)      */   &Nbsp;public  $path;       public function getabsolutepath ()     {         return null ===  $this->path              ? null              :  $this->getuploadrootdir (). '
/'. $this->path;    &NBSP}       public function getwebpath ()   
  {        return null ===  $this->path             ? null              :  $this->getuploaddir (). '
/'. $this->path;    &NBSP}       protected function getuploadrootdir ()      {  &NBsp;     // the absolute directory path where uploaded         // documents should be saved          return __dir__. ' /.. /.. /.. /..
/web/'. $this->getuploaddir ();    &NBSP}       protected function getuploaddir ()      {        // get rid of the __dir_ _ SO IT DOESN ' T screw up         // when
 displaying uploaded doc/image in the view.
        return  ' uploads/documents ';    &NBSP}}

The document entity has a name associated with the file. This path property stores the relative path of a file and is stored in the database. This getabsolutepath () returns an absolute path, and Getwebpath () returns a web path for the template to join the upload file link.


If you haven't done so, you should read http://symfony.com/doc/current/reference/forms/types/file.html first to understand the basic upload process.

If you use annotations to validate a rule (as shown in this example), make sure that you have enabled annotation validation (see Http://symfony.com/doc/current/book/validation.html#book-validation-configuration).

When processing an actual file upload, use a "virtual" file field. For example, if you build a form directly in controller, he might be like this:


Public function uploadaction () {    //        $ form =  $this->createformbuilder ($document)         -> Add (' name ')         ->add (' file ')      
   ->getform ();
      //&NBSP ...}
Next, create the File attribute into your document class and add some validation rules: use symfony\component\httpfoundation\file\uploadedfile;  //&nbsp class document {    /**      *  @Assert \
File (maxsize= "6000000")      */    private  $file;
      /**      * sets file.      *      *  @param  UploadedFile  $file       */    public function setFile (uploadedfile  $file  = null)     {      
   $this->file =  $file;
   &NBSP}       /**      * get file.      *      *  @return  uploadedfile       */    public function getfile ()     { 
       return  $this->file;    &NBSP}} annotations annotations// src/acme/demobundle/entity/document.php namespace 
acme\demobundle\entity;
 //&NBSP ... use symfony\component\validator\constraints as assert;   Class document {    /**      *  @Assert \file (maxSize
= "6000000")      */    private  $file;   &NBSp;   // .} 

When you use the file constraint, Symfony automatically guesses that the form field is entered as a file upload. This is why when you create a form (->add (' file ')), why it is not explicitly set in the form as a file upload.

The following controller tells you how to handle the whole process:


  use acme\demobundle\entity\document;.
use sensio\bundle\frameworkextrabundle\configuration\template;
use symfony\component\httpfoundation\request; &nbsp  /**  *  @Template ()  */public function uploadaction (request $ Request) {     $document  = new document ()      $form  
=  $this->createformbuilder ($document)         ->add (' name ')         ->add (' file ')        
 ->getform ();
       $form->handlerequest ($request);       if  ($form->isvalid ())  {       
  $em  =  $this->getdoctrine ()->getmanager ();
           $em->persist ($document);          $em->flush ();           return  $this->redirect ($this->generateurl (...)
));    &NBSP}       return array (' form '  =>  $form->
CreateView ()); }

The previous controller when the name automatic Storage document entity was submitted, but he would not do anything about the file and the path attribute would be blank.


A simple way to process file uploads is to set the path attribute before entity is persisted. When handling file uploads at some point, you call the document entity class with a upload () method that assigns a value to the path.


if  ($form->isvalid ())  {     $em  =  $this->getdoctrine ()->
GetManager ();
       $document->upload ();
       $em->persist ($document);
     $em->flush ();
      return  $this->redirect (...); This upload () method utilizes the UploadedFile object, which returns the file field after it is committed: Public function upload () {    //  the file property can be empty if the field is not  required     //  The File property is empty This property does not need     if  (null ===   $this->getfile ())  {        return;     &NBSP}       // use the original file name here but  you should     // SANITIZE IT AT LEAST TO AVOID&NBSp;any security issues     //   Here you should use the original filename but should at least audit it to avoid some security issues      // move takes the target directory and then the      // target filename to move to     //  Move the target file to the target directory      $this->getfile ()->move (          $this->getuploadrootdir (),          $this->getfile ()->
Getclientoriginalname ()     );       // set the path property to the filename  where you ' Ve saved the file     //  Set the Path property to save the file name for you  
    $this->path =  $this->getfile ()->getclientoriginalname ();       // CLEAN UP THE FILE PROPERTY AS  You won ' T need it anymore     //  clean up the file properties you don't need    
  $this->file = null;   Using the lifecycle callback lifecycle callback is a limited technique, and he has some drawbacks. If you want to remove the write-dead encoding __dir__ in the Document::getuploadrootdir () method, the best approach is to start using doctrine listeners.
Where you will be able to inject kernel parameters, such as Kernel.root_dir, to establish an absolute path. Working on this principle, he has a flaw: what is the problem when entity is persisted?
A: The file has been transferred to its final location, and the path attribute under the entity class cannot be properly materialized. (If Entity has persistent problems or files cannot be moved and nothing happens) in order to avoid these problems, you should change the implementation so that the database operates and automatically deletes files:/**  *  @ORM \entity  *  @ORM \haslifecyclecallbacks  */class document {}


Next, use these callback functions to refactor the document class:


use symfony\component\httpfoundation\file\uploadedfile;  /**  *  @ORM \entity  *  @ORM \haslifecyclecallbacks  */class document { 
   private  $temp;
      /**      * sets file.      *      *  @param  UploadedFile  $file       */    public function setfile (UploadedFile  $file  = null)     {         $this->file
 =  $file;         // check if we have an old  Image path         //  Check if we have an old picture path          if  (Isset ($this->path))  {            &nbSp;// store the old name to delete after the update   
           $this->temp =  $this->path;
             $this->path = null;         } else {       
      $this->path =  ' initial ';
       &NBSP}     }       /**
     *  @ORM \prepersist ()      *  @ORM \preupdate ()      */    public function preupload ()      {        if  (null !==  $this->getfile ())  {          &Nbsp; // do whatever you want to generate a unique name             //  to generate a unique name               $filename  = sha1 (Uniqid (Mt_rand (),  true)
;              $this->path =  $filename.
$this->getfile ()->guessextension ();
       &NBSP}     }       /**
     *  @ORM \postpersist ()      *  @ORM \postupdate ()      */    public function upload ()    
 {        if  (null ===  $this->getfile ())  {             reTurn        &NBSP}           //  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         //an exception move () throws an exception automatically when the file is moved incorrectly.
        //this will prevent an entity from persisting an error in the database.          $this->getfile ()->move ($this->getuploadrootdir (),
  $this->path);           // 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;        &NBSP}          $this->
file = null;    &NBSP}       /**      *  @ORM \postremove ()      */    public function removeupload ()   
  {         $file  =  $this->getabsolutepath ();         if  ($file)  {          
  unlink ($file);         }    &nbsp}}

If you change your entity is handled by doctrine event listener or event subscriber, this preupdate () callback function must notify doctrine about the changes being made. For a complete reference to the Preupdate event limit, see the Http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/events.html#preupdate

Now this class does everything you need: He will generate a unique filename before entity is persisted, move the file, and delete the file.


Moving the file now is entity automatically, and this $document->upload () should be removed from the controller:


if ($form->isvalid ()) {
$em = $this->getdoctrine ()->getmanager ();
$em->persist ($document);
$em->flush ();
return
$this->redirect (...);
}

This @orm\prepersist () and @orm\postpersist () event callback: One is triggered before the entity is persisted to the database, and one is triggered after the entity is persisted to the database. On the other hand, @ORM \preupdate () and @ORM \postupdate () event callbacks are triggered when an entity is updated.

The Preupdate and postupdate callbacks are triggered when the entity field is changed after the persistence operation. This means that by default you only change the $file property, and these events will not be triggered because the property itself will not be persisted to doctrine. One workaround is to create a updated field to persist it to doctrine and adjust it manually when the file changes.

Use ID as file name

If you want to use an ID as a file name, and the implementation is slightly different, you need to save the Path property to the file extension instead of the actual file name:


use symfony\component\httpfoundation\file\uploadedfile;  /**  *  @ORM \entity  *  @ORM \haslifecyclecallbacks  */class document { 
   private  $temp;
      /**      * sets file.      *      *  @param  UploadedFile  $file       */    public function setfile (UploadedFile  $file  = null)     {         $this->file
 =  $file;         // check if we have an old  Image path         if  (Is_file ($this->getabsolutepath ()))  {            // store the old  name to d elete after the update            
  $this->temp =  $this->getabsolutepath ();         } else {       
      $this->path =  ' initial ';
       &NBSP}     }       /**
     *  @ORM \prepersist ()      *  @ORM \preupdate ()      */    public function preupload ()      {        if  (null !==  $this->getfile ())  {             $this->path = $
This->getfile ()->guessextension ();         }     }       /**      *  @ORM \postpersist ()       *  @ORM \postupdate ()      */    public  Function upload ()     {        if  (null  ===  $this->getfile ())  {           
 return;        &NBSP}           //  check if we have an old image          if  (Isset ($this->temp))  {             // delete the old image           
  unlink ($this->temp);             // clear the temp image path          
    $this->temp = null;        &NBSP}           //  you must throw an exception here if the file cannot be  moved         // so that the entity is  not persisted to the database         //  which the uploadedfile move ()  method does           $this->getfile ()->move (              $this->getuploadrootdir (),             $ This->id. '. '. $this->getfile ()->guessextension ()         );
           $this->setfile (null);    &NBSP}       /**      *  @ORM \preremove (
)      */    public function storefilenameforremove ()     {         $this->temp =  $this->
GetAbsolutePath ();    &NBSP}       /**      *  @ORM \postremove ()      */    public function removeupload ()      {        if  (isset ($this->temp))  { 
           unlink ($this->temp);        &NBSP}     }       public  fUnction getabsolutepath ()     {        return  null ===  $this->path              ? null             :  $this-> Getuploadrootdir (). ' /'. $this->id. '.
$this->path;    &NBSP}}

You will notice that in this case you need to do a little work to delete the file. Before the data is deleted, you must save the file path (because it depends on the ID). Then, once the object has been completely removed from the database, you can safely delete the file (after the data has been deleted).


Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.