Skip to content

Instantly share code, notes, and snippets.

@beberlei
Created May 18, 2011 10:46
Show Gist options
  • Star 46 You must be signed in to star a gist
  • Fork 16 You must be signed in to fork a gist
  • Save beberlei/978346 to your computer and use it in GitHub Desktop.
Save beberlei/978346 to your computer and use it in GitHub Desktop.
My Symfony2 File Upload workflow
<?php
use Symfony\Component\HttpFoundation\File\File;
use Symfony\Component\HttpFoundation\File\UploadedFile;
/**
* @Entity
*/
class Document
{
/** @var File - not a persistent field! */
private $file;
/** @var string
* @Column(type="string")
*/
private $filePersistencePath;
/** @var string */
protected static $uploadDirectory = null;
static public function setUploadDirectory($dir)
{
self::$uploadDirectory = $dir;
}
static public function getUploadDirectory()
{
if (self::$uploadDirectory === null) {
throw new \RuntimeException("Trying to access upload directory for profile files");
}
return self::$uploadDirectory;
}
/**
* Assumes 'type' => 'file'
*/
public function setFile(File $file)
{
$this->file = $file;
}
public function getFile()
{
return new File(self::getUploadDirectory() . "/" . $this->filePersistencePath);
}
public function getFilePersistencePath()
{
return $this->filePersistencePath;
}
public function processFile()
{
if (! ($this->file instanceof UploadedFile) ) {
return false;
}
$uploadFileMover = new UploadFileMover();
$this->filePersistencePath = $uploadFileMover->moveUploadedFile($this->file, self::getUploadedDirectory());
}
}
<?php
class DocumentController extends Controller
{
public function formAction()
{
$document = new Document();
$form = $this->container->get('form.factory')->create(new DocumentType(), $document);
$request = $this->container->get('request');
if ($request->getMethod() == 'POST') {
if ($form->isValid()) {
$document->processFile();
}
}
}
}
<?php
class UploadFileMover
{
public function moveUploadedFile(UploadedFile $file, $uploadBasePath)
{
$originalName = $file->getOriginalName();
// use filemtime() to have a more determenistic way to determine the subpath, otherwise its hard to test.
$relativePath = date('Y-m', filemtime($this->file->getPath()));
$targetFileName = $relativePath . DIRECTORY_SEPARATOR . $originalName;
$targetFilePath = $uploadBasePath . DIRECTORY_SEPARATOR . $targetFileName;
$ext = $this->file->getExtension();
$i = 1;
while (file_exists($targetFilePath) && md5_file($file->getPath()) != md5_file($targetFilePath)) {
if ($ext) {
$prev = $i==1 ? "" : $i;
$targetFilePath = $targetFilePath . str_replace($prev . $ext, $i++ . $ext, $targetFilePath);
} else {
$targetFilePath = $targetFilePath . $i++;
}
}
$targetDir = $uploadBasePath . DIRECTORY_SEPARATOR . $relativePath;
if (!is_dir($targetDir)) {
$ret = mkdir($targetDir, umask(), true);
if (!$ret) {
throw new \RuntimeException("Could not create target directory to move temporary file into.");
}
}
$file->move($targetDir, basename($targetFilePath));
return str_replace($uploadBasePath . DIRECTORY_SEPARATOR, "", $targetFilePath);
}
}
@xaptronic
Copy link

Hi, nice example.. where do you call Document::setUploadDirectory?

@beberlei
Copy link
Author

in the bundle boot method.

@ardianys
Copy link

ardianys commented Oct 5, 2012

Thanks, useful !

@bicpi
Copy link

bicpi commented Oct 27, 2012

Thanks for this really nice approach.

What I like the most is the static uploads directory path which is set during the bundle bootup. This is a simple way to put this setting in the application parameters.

# app/config/config.yml
parameters:
    uploads_directory: %kernel.root_dir%/../uploads
# MyBundle.php
// ...
    public function boot()
    {
        Document::setUploadsDirectory($this->container->getParameter('uploads_directory'));
    }
}
// ...

This can be useful for any kind of entity parameters and there are a lot of questions out there how to get values from the parameters.yml from inside an entity.

@v3labs
Copy link

v3labs commented Jan 14, 2013

@moesis, Read the Symfony2 forms documentation. You'll quickly figure out what the DocumentType class should contain :)

@fr43nk
Copy link

fr43nk commented Feb 4, 2013

Hi,

is it by intention, using the $this->file object instead of the $file object in the moveUploadedFile method?

best regards.

@iandroogmans
Copy link

The only problem i've had with this approach: defining different folders for each bundle.

Multiple bundles are always loaded so you can't define a variable upload directory for each bundle, the directory will always be overwritten by the last bundle loaded.

@phamios
Copy link

phamios commented Aug 19, 2015

could you show me the template TWIG when you implements this form upload ?

@atompulse
Copy link

I really really dont understand what this gist has anything to do with uploading a file in symfony?!?!
Why do I need an entity to upload a file in symfony?
The author definitively didnt understand symfony OR he only follows blindly some examples which he clearly doesnt grasp.
Good job! Keep up the noob work!

@niketpathak
Copy link

niketpathak commented Nov 21, 2017

The entity is obviously needed to store/persist a reference of the uploaded asset. How else will you access the uploaded file in your code? If you do see another/better way, kindly post it instead of criticizing the author.
P.s. I needed to integrate symfony with a JS library to generate thumbnails of the uploaded images. This article helped me and might help some of you too! :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment