Buy Access to Course

Where & How to Store the File

Share this awesome video!


For now, the form is still submitting to this test endpoint. We'll change that soon by moving it into the actual article form. But, to finish a successful file upload, we need move the uploaded file from the temporary spot on the filesystem to its final location.

Where to Store Uploads?

So... where should we store the uploaded article images? The first question to ask is: can these uploaded files be public to everyone? Or do we need to do some sort of security check before a user can view or download them? For article images, they can be public. But we'll talk about private files later.

Ok, so if someone needs to be able to view these images, it means they need to live somewhere in the public/ directory. Later, we're going to talk about storing files in the cloud! Like S3, which honestly, is an awesome idea. But right now, we're going to keep it simple and store things directly on our server.

So how about storing things in... I don't know... public/uploads? Create that new directory. Then, inside, create an empty .gitignore file. The reason I'm doing this might be confusing at first. My goal is to ignore any files added to this directory from git... because we don't want to commit uploaded files. But I would also like to make sure that this directory at least exists when I clone the repository.

Find your terminal and add the empty .gitignore file:

git add public/uploads/.gitignore

Next, open up the real .gitignore file - the one at the root of your app - and ignore the entire /public/uploads directory. It's a bit weird, but thanks to this, we will ignore all files in public/uploads except for the .gitignore file we already added.

20 lines | .gitignore
// ... lines 1 - 17

Why did we do this? Well, unfortunately, you can't add a directory to git. So by adding this .gitignore file, it will guarantee that the public/uploads directory will exist when you clone the repository. Honestly, the file could be named anything, it's just sort of a common practice to use an empty .gitignore file for this.

Check it out: create a new file in public/uploads called foo. Then, find your terminal and run:

git status

We see the new public/uploads/.gitignore file but we do not see the foo file. That's perfect. Delete that.

Moving the Uploaded File

Let's get to work inside of our controller to move the file. First, set the uploaded file to a new $uploadedFile variable. And, unfortunately, the phpdoc on this get() method is a bit generic... so it doesn't tell our editor that this will be an UploadedFile object. Because I'm obsessed with auto-completion, let's add inline doc about this: this will be an UploadedFile object - but not the one from Guzzle - the one from HttpFoundation in Symfony.

120 lines | src/Controller/ArticleAdminController.php
// ... lines 1 - 10
use Symfony\Component\HttpFoundation\File\UploadedFile;
// ... lines 12 - 15
class ArticleAdminController extends BaseController
// ... lines 18 - 73
public function temporaryUploadAction(Request $request)
/** @var UploadedFile $uploadedFile */
$uploadedFile = $request->files->get('image');
// ... lines 78 - 79
// ... lines 81 - 118

And guess what? This UploadedFile object has a super useful method on it: move()! Give it the destination directory and it'll take care of the rest. To get that directory, say $destination = and we need to get the path to our uploads/ directory. The best way is to read a parameter: $this->getParameter('kernel.project_dir') - to get the absolute path to the root of the app - then /public/uploads. Then add $uploadedFile->move() and pass it $destination.

Hold Command or Ctrl and click this method. Ah, it returns a File object that represents the new file. Let's see what this looks like: surround this entire call with dd().

120 lines | src/Controller/ArticleAdminController.php
// ... lines 1 - 73
public function temporaryUploadAction(Request $request)
// ... lines 76 - 77
$destination = $this->getParameter('kernel.project_dir').'/public/uploads';
// ... lines 81 - 120

Alright team! Find your browser, refresh and re-post that upload. I... think it worked! The dumped file object tells me that there is a new file in our public/uploads/ directory. Let's go check it out! There it is! Well, I think that's it... but sheesh - the filename is terrible. Let's check its file size:

ls -la public/uploads/

Yea... that looks correct - it's about 1.8 megabytes. So... we moved the file... but that is a terrible filename. Let's fix that next.