Doctrine是基于数据库抽像层上的ORM,它可以通过PHP对象轻松访问所有的数据库,例如MYSQL,它支持的PHP最低版本为5.2.3,下面我们一起来看看Doctrine文件上传处理例子,希望文章对各位有帮助.
	
		基本设置,创建一个简单的Doctrine实体类:
	
		
			- 
				 
- 
				namespace Acme\DemoBundle\Entity; 
- 
				   
- 
				use Doctrine\ORM\Mapping as ORM; 
- 
				use Symfony\Component\Validator\Constraints as Assert; 
- 
				   
- 
				 
- 
				 
- 
				 
- 
				class Document 
- 
				{ 
- 
				     
- 
				 
- 
				 
- 
				 
- 
				 
- 
				    public $id; 
- 
				   
- 
				     
- 
				 
- 
				 
- 
				 
- 
				    public $name; 
- 
				   
- 
				     
- 
				 
- 
				 
- 
				    public $path; 
- 
				   
- 
				    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() 
- 
				    { 
- 
				         
- 
				         
- 
				        return __DIR__.'/../../../../web/'.$this->getUploadDir(); 
- 
				    } 
- 
				   
- 
				    protected function getUploadDir() 
- 
				    { 
- 
				         
- 
				         
- 
				        return 'uploads/documents'; 
- 
				    } 
- 
				} 
	
		该document实体有一个名称与文件相关联,这个path属性存储一个文件的相对路径并且在数据库中存储,这个getAbsolutePath()会返回一个绝对路径,getWebPath()会返回一个web路径,用于模板加入上传文件链接.
	
		如果你还没有这样做的话,你应该阅读http://symfony.com/doc/current/reference/forms/types/file.html首先了解基本的上传过程.
	
		如果您使用注释来验证规则(如本例所示),请确保你启用了注释验证(见http://symfony.com/doc/current/book/validation.html#book-validation-configuration).
	
		在处理一个实际的文件上传时,使用一个“虚拟”的file字段,例如,如果你在controller中直接构建一个form,他可能是这样的.
	
		
			- 
				public function uploadAction() 
- 
				{ 
- 
				     
- 
				   
- 
				    $form = $this->createFormBuilder($document) 
- 
				        ->add('name') 
- 
				        ->add('file') 
- 
				        ->getForm(); 
- 
				   
- 
				     
- 
				} 
	
		下一步,创建file这个属性到你的Document类中并且添加一些验证规则:
	
		
			- 
				use Symfony\Component\HttpFoundation\File\UploadedFile; 
- 
				   
- 
				 
- 
				class Document 
- 
				{ 
- 
				     
- 
				 
- 
				 
- 
				    private $file; 
- 
				   
- 
				     
- 
				 
- 
				 
- 
				 
- 
				 
- 
				    public function setFile(UploadedFile $file = null) 
- 
				    { 
- 
				        $this->file = $file; 
- 
				    } 
- 
				   
- 
				     
- 
				 
- 
				 
- 
				 
- 
				 
- 
				    public function getFile() 
- 
				    { 
- 
				        return $this->file; 
- 
				    } 
- 
				} 
- 
				annotations 
- 
				Annotations 
- 
				 
- 
				namespace Acme\DemoBundle\Entity; 
- 
				   
- 
				 
- 
				use Symfony\Component\Validator\Constraints as Assert; 
- 
				   
- 
				class Document 
- 
				{ 
- 
				     
- 
				 
- 
				 
- 
				    private $file; 
- 
				   
- 
				     
- 
				} 
	
		当你使用File约束,symfony会自动猜测表单字段输入的是一个文件上传,这就是当你创建表单(->add(‘file’))时,为什么没有在表单明确设置为文件上传的原因.
	
		下面的控制器,告诉您如何处理全部过程:
	
		
			- 
				 
- 
				use Acme\DemoBundle\Entity\Document; 
- 
				use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template; 
- 
				use Symfony\Component\HttpFoundation\Request; 
- 
				 
- 
				   
- 
				 
- 
				 
- 
				 
- 
				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(...)); 
- 
				    } 
- 
				   
- 
				    return array('form' => $form->createView()); 
- 
				} 
	
		以前的controller当提交name自动的存储Document实体,但是他不会做任何关于文件的事情并且path属性也将是空白.
	
		处理文件上传一个简单的方法就是在entity持久化之前设置相应的path属性,在某一时刻处理文件上传时,要调用Document实体类一个upload()方法给path赋值.
	
		
			- 
				if ($form->isValid()) { 
- 
				    $em = $this->getDoctrine()->getManager(); 
- 
				   
- 
				    $document->upload(); 
- 
				   
- 
				    $em->persist($document); 
- 
				    $em->flush(); 
- 
				   
- 
				    return $this->redirect(...); 
- 
				} 
	
		这个upload()方法利用UploadedFile对象,是它提交后返回file字段:
	
		
			- 
				public function upload() 
- 
				{ 
- 
				     
- 
				     
- 
				    if (null === $this->getFile()) { 
- 
				        return; 
- 
				    } 
- 
				   
- 
				     
- 
				     
- 
				     
- 
				     
- 
				     
- 
				     
- 
				    $this->getFile()->move( 
- 
				        $this->getUploadRootDir(), 
- 
				        $this->getFile()->getClientOriginalName() 
- 
				    ); 
- 
				   
- 
				     
- 
				     
- 
				    $this->path = $this->getFile()->getClientOriginalName(); 
- 
				   
- 
				     
- 
				     
- 
				    $this->file = null; 
- 
				} 
	
		使用生命周期回调:
	
		生命周期回调是一种有限的技术,他有一些缺点,如果你想移除Document::getUploadRootDir()方法里的写死的编码__DIR__,最好的方法是开始使用Doctrine listeners,在哪里你将能够注入内核参数,如kernel.root_dir来建立绝对路径.
	
		这种原理工作,他有一个缺陷:也就是说当entity持久化时会有什么问题呢?答:该文件已经转移到了它的最终位置,实体类下的path属性不能够正确的实体化.
	
		如果entity有持久化问题或者文件不能够移动,什么事情也没有发生,为了避免这些问题,你应该改变这种实现方式以便数据库操作和自动删除文件:
	
		
			- 
				 
- 
				 
- 
				 
- 
				 
- 
				class Document 
- 
				{ 
- 
				} 
	
		接下来,利用这些回调函数重构Document类:
	
		
			- 
				use Symfony\Component\HttpFoundation\File\UploadedFile; 
- 
				   
- 
				 
- 
				 
- 
				 
- 
				 
- 
				class Document 
- 
				{ 
- 
				    private $temp; 
- 
				   
- 
				     
- 
				 
- 
				 
- 
				 
- 
				 
- 
				    public function setFile(UploadedFile $file = null) 
- 
				    { 
- 
				        $this->file = $file; 
- 
				         
- 
				         
- 
				        if (isset($this->path)) { 
- 
				             
- 
				            $this->temp = $this->path; 
- 
				            $this->path = null; 
- 
				        } else { 
- 
				            $this->path = 'initial'; 
- 
				        } 
- 
				    } 
- 
				   
- 
				     
- 
				 
- 
				 
- 
				 
- 
				    public function preUpload() 
- 
				    { 
- 
				        if (null !== $this->getFile()) { 
- 
				             
- 
				             
- 
				            $filename = sha1(uniqid(mt_rand(), true)); 
- 
				            $this->path = $filename.'.'.$this->getFile()->guessExtension(); 
- 
				        } 
- 
				    } 
- 
				   
- 
				     
- 
				 
- 
				 
- 
				 
- 
				    public function upload() 
- 
				    { 
- 
				        if (null === $this->getFile()) { 
- 
				            return; 
- 
				        } 
- 
				   
- 
				         
- 
				         
- 
				         
- 
				         
- 
				         
- 
				        $this->getFile()->move($this->getUploadRootDir(), $this->path); 
- 
				   
- 
				         
- 
				        if (isset($this->temp)) { 
- 
				             
- 
				            unlink($this->getUploadRootDir().'/'.$this->temp); 
- 
				             
- 
				            $this->temp = null; 
- 
				        } 
- 
				        $this->file = null; 
- 
				    } 
- 
				   
- 
				     
- 
				 
- 
				 
- 
				    public function removeUpload() 
- 
				    { 
- 
				        $file = $this->getAbsolutePath(); 
- 
				        if ($file) { 
- 
				            unlink($file); 
- 
				        } 
- 
				    } 
- 
				} 
	
		如果更改你的entity是由Doctrine event listener 或event subscriber处理,这个 preUpdate()回调函数必须通知Doctrine关于正在做的改变,有关preUpdate事件限制的完整参考请查看 http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/events.html#preupdate
	
		现在这个类做了你需要的一切:他会在entity持久化之前生成一个唯一的文件名,持久化之后,移动文件,删除文件.
	
		现在移动文件是entity自动完成,这个$document->upload()就应该从controller中移除了:
	
		
			- 
				if ($form->isValid()) { 
- 
				    $em = $this->getDoctrine()->getManager(); 
- 
				   
- 
				    $em->persist($document); 
- 
				    $em->flush(); 
- 
				   
- 
				    return $this->redirect(...); 
- 
				} 
	
		这个@ORM\PrePersist()和@ORM\PostPersist()事件回调:一个是在entity持久化到数据库之前触发,一个是在entity持久化到数据库之后触发。另一方面, @ORM\PreUpdate() 和 @ORM\PostUpdate()事件回调时当实体更新时触发。
	
		当改变entity字段后进行持久化操作时,PreUpdate和PostUpdate回调才会被触发。这意味着,默认情况下,你只改变了$file属性,这些事件不会被触发,因为这个属性它自己不会持久化到Doctrine。有一个解决方法,就是创建一个updated字段把它持久化到Doctrine,并当文件改变时手动调整它。
	
		使用ID作为文件名
	
		如果要使用ID作为文件名,实现略有不同,您需要保存path属性为文件扩展名,而不是实际的文件名:
	
		
			- 
				use Symfony\Component\HttpFoundation\File\UploadedFile; 
- 
				   
- 
				 
- 
				 
- 
				 
- 
				 
- 
				class Document 
- 
				{ 
- 
				    private $temp; 
- 
				   
- 
				     
- 
				 
- 
				 
- 
				 
- 
				 
- 
				    public function setFile(UploadedFile $file = null) 
- 
				    { 
- 
				        $this->file = $file; 
- 
				         
- 
				        if (is_file($this->getAbsolutePath())) { 
- 
				             
- 
				            $this->temp = $this->getAbsolutePath(); 
- 
				        } else { 
- 
				            $this->path = 'initial'; 
- 
				        } 
- 
				    } 
- 
				   
- 
				     
- 
				 
- 
				 
- 
				 
- 
				    public function preUpload() 
- 
				    { 
- 
				        if (null !== $this->getFile()) { 
- 
				            $this->path = $this->getFile()->guessExtension(); 
- 
				        } 
- 
				    } 
- 
				   
- 
				     
- 
				 
- 
				 
- 
				 
- 
				    public function upload() 
- 
				    { 
- 
				        if (null === $this->getFile()) { 
- 
				            return; 
- 
				        } 
- 
				   
- 
				         
- 
				        if (isset($this->temp)) { 
- 
				             
- 
				            unlink($this->temp); 
- 
				             
- 
				            $this->temp = null; 
- 
				        } 
- 
				   
- 
				         
- 
				         
- 
				         
- 
				        $this->getFile()->move( 
- 
				            $this->getUploadRootDir(), 
- 
				            $this->id.'.'.$this->getFile()->guessExtension() 
- 
				        ); 
- 
				   
- 
				        $this->setFile(null); 
- 
				    } 
- 
				   
- 
				     
- 
				 
- 
				 
- 
				    public function storeFilenameForRemove() 
- 
				    { 
- 
				        $this->temp = $this->getAbsolutePath(); 
- 
				    } 
- 
				   
- 
				     
- 
				 
- 
				 
- 
				    public function removeUpload() 
- 
				    {   
- 
				        if (isset($this->temp)) { 
- 
				            unlink($this->temp); 
- 
				        } 
- 
				    } 
- 
				   
- 
				    public function getAbsolutePath() 
- 
				    { 
- 
				        return null === $this->path 
- 
				            ? null 
- 
				            : $this->getUploadRootDir().'/'.$this->id.'.'.$this->path; 
- 
				    } 
- 
				} 
	
		你会注意到,在这种情况下,你需要做一点工作,以删除该文件,在数据删除之前,你必须保存文件路径(因为它依赖于ID),然后,一旦对象已经完全从数据库中删除,你就可以安全的删除文件(在数据删除之后).