Doctrine是基于数据库抽像层上的ORM,它可以通过PHP对象轻松访问所有的数据库,例如MYSQL,它支持的PHP最低版本为5.2.3,下面我们一起来看看Doctrine文件上传处理例子,希望文章对各位有帮助.
基本设置,创建一个简单的Doctrine实体类:
// 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) */ 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() { // 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 up // when displaying uploaded doc/image in the view. 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 { /** * @Assert\File(maxSize="6000000") */ private $file ; /** * Sets file. * * @param UploadedFile $file */ public function setFile(UploadedFile $file = null) { $this ->file = $file ; } /** * Get file. * * @return UploadedFile */ public function getFile() { return $this ->file; } } annotations Annotations // src/Acme/DemoBundle/Entity/Document.php namespace Acme\DemoBundle\Entity; // ... use Symfony\Component\Validator\Constraints as Assert; class Document { /** * @Assert\File(maxSize="6000000") */ private $file ; // ... }当你使用File约束,symfony会自动猜测表单字段输入的是一个文件上传,这就是当你创建表单(->add(‘file’))时,为什么没有在表单明确设置为文件上传的原因.
下面的控制器,告诉您如何处理全部过程:
// ... use Acme\DemoBundle\Entity\Document; use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template; use Symfony\Component\HttpFoundation\Request; // ... /** * @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(...)); } 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() { // the file property can be empty if the field is not required // 该file属性为空这个属性就不需要了 if (null === $this ->getFile()) { return ; } // use the original file name here but you should // sanitize it at least to avoid any security issues // 这里你应该使用原文件名但是应该至少审核它避免一些安全问题 // move takes the target directory and then the // target filename to move to // 将目标文件移动到目标目录 $this ->getFile()->move( $this ->getUploadRootDir(), $this ->getFile()->getClientOriginalName() ); // set the path property to the filename where you've saved the file // 设置path属性为你保存文件的文件名 $this ->path = $this ->getFile()->getClientOriginalName(); // clean up the file property as you won't need it anymore // 清理你不需要的file属性 $this ->file = null; }使用生命周期回调:
生命周期回调是一种有限的技术,他有一些缺点,如果你想移除Document::getUploadRootDir()方法里的写死的编码__DIR__,最好的方法是开始使用Doctrine listeners,在哪里你将能够注入内核参数,如kernel.root_dir来建立绝对路径.
这种原理工作,他有一个缺陷:也就是说当entity持久化时会有什么问题呢?答:该文件已经转移到了它的最终位置,实体类下的path属性不能够正确的实体化.
如果entity有持久化问题或者文件不能够移动,什么事情也没有发生,为了避免这些问题,你应该改变这种实现方式以便数据库操作和自动删除文件:
/** * @ORM\Entity * @ORM\HasLifecycleCallbacks */ class Document { }接下来,利用这些回调函数重构Document类:
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 (isset( $this ->path)) { // store the old name to delete after the update $this ->temp = $this ->path; $this ->path = null; } else { $this ->path = 'initial' ; } } /** * @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 ->path = $filename . '.' . $this ->getFile()->guessExtension(); } } /** * @ORM\PostPersist() * @ORM\PostUpdate() */ public function upload() { if (null === $this ->getFile()) { 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 //当移动文件发生错误,一个异常move()会自动抛出异常。 //这将阻止实体持久化数据库发生错误。 $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; } $this ->file = null; } /** * @ORM\PostRemove() */ 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; /** * @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 delete after the update $this ->temp = $this ->getAbsolutePath(); } else { $this ->path = 'initial' ; } } /** * @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 ; } // 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; } // 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); } /** * @ORM\PreRemove() */ public function storeFilenameForRemove() { $this ->temp = $this ->getAbsolutePath(); } /** * @ORM\PostRemove() */ public function removeUpload() { //phpfensi.com if (isset( $this ->temp)) { unlink( $this ->temp); } } public function getAbsolutePath() { return null === $this ->path ? null : $this ->getUploadRootDir(). '/' . $this ->id. '.' . $this ->path; } }你会注意到,在这种情况下,你需要做一点工作,以删除该文件,在数据删除之前,你必须保存文件路径(因为它依赖于ID),然后,一旦对象已经完全从数据库中删除,你就可以安全的删除文件(在数据删除之后).
查看更多关于Doctrine文件上传处理例子_php上传教程的详细内容...
声明:本文来自网络,不代表【好得很程序员自学网】立场,转载请注明出处:http://www.haodehen.cn/did29294