Review Upload Flow0:00
OK, we have Vue set up, and we have FilePond installed and rendered in our front end. We're able to drag and drop images, but we need to get them into our application in order to actually save them. Let's open up our source code. And earlier we created this upload route that FilePond is sending the image data to. It's using our ImageController, so let's open that up. And take a look at the store method. So our image data is coming through here, and we'll need to do a few things in order to save that image, so let's break them down. First we need to validate the incoming file.
to save that image, so let's break them down. First we need to validate the incoming file. And after that's done, we need to actually process the upload and save the file in storage. Then we need to create an Image model for it. And finally, return that Image model back to the front end. That way our Vue application can make use of it. Alright let's take these steps one at a time. So for validation, if we want something simple, we can just run a conditional saying request()->hasFile(). And this expects a name attribute associated with the file element. Now if we go back to our App.vue component, we named the FilePond component image.
Validate Uploaded Image1:12
And this expects a name attribute associated with the file element. Now if we go back to our App.vue component, we named the FilePond component image. And that's the same name that the image data will be passed in under. So if the request does not have a file called image, then we're going to just return a response back, an error that says there is no image present. And we'll give it a code of 400, since it's a client-side error. We can also do some further validation by using Laravel's built-in validator on the request. We're only passing in one data attribute, and that's the image. And there's a few file-oriented validation rules that we can use here. So the image has to be a file.
And there's a few file-oriented validation rules that we can use here. So the image has to be a file. It has to be an image file. And it's required. Actually I like putting the required first. Using this image validation rule, if we take a look at the documentation, the file under validation must be an image, so the request will return back an error unless the file is a JPEG, PNG, or one of these other types. Now we had already added in some validation on our frontend with this acceptedFileTypes attribute here.
Store File in Storage2:36
Now we had already added in some validation on our frontend with this acceptedFileTypes attribute here. So right now FilePond should only be accepting image files, which matches what we have in the backend now. That looks good, so now we can move on to actually saving the file in our app's storage. This is really straightforward with Laravel, and all we have to do is call request()->file('image')->store() method. This expects a path that we're going to be storing the image in, and it's relative to our app's storage/app directory. So if we use something like public/images, that means that our images will be stored.
our app's storage/app directory. So if we use something like public/images, that means that our images will be stored in the public/images folder underneath the storage/app folder in our application. This also returns false if it didn't save, or a fully qualified path to the file that was stored. So we can then run a conditional on this if there is no path. Then we're going to return a response back again, error, the file could not be saved. And because this is a server-side error, we'll give it a 500 code. Okay so our image is validated, saved, and now we need to create a model to store its information in our database.
Create Image Database Record3:49
Okay so our image is validated, saved, and now we need to create a model to store its information in our database. So first let's add that model class at the top, use App\Models\Image. So now we can call image::create, and pass in our array of attributes that the table and the database expects. Thinking back to our migration, that was a name, an extension, and a size. These can all be pulled in from the request->file method, passing in the file name, image, and this returns back an UploadedFile class from Laravel. So we can say $uploadedFile equals request()->file('image'), and then use this $uploadedFile object and some methods and properties associated with it in order to fill this information in.
So we can say UploadedFile equals request()->file('image'), and then use this UploadedFile object and some methods and properties associated with it in order to fill this information in. So for the name, we can say UploadedFile::hashName(), and we're using hashName() because when we call this store() method here, Laravel automatically replaces the name that the file was when the user uploaded it with a randomly generated one. And that's the name that we want because that's what's being stored in our application. For the extension, it's also just as easy, UploadedFile::extension(), and the size, UploadedFile::getSize(), which returns the file size in bytes. Okay, once that's saved, we'll get back an Image model object, and that's what we want to return back to our front end, so just return $image.
Debug CSRF 419 Error5:25
Okay, once that's saved, we'll get back an Image model object, and that's what we want to return back to our front end, so just return image. Alright so let's open up our browser and test this out. Okay let's find an image to upload. How about this one? And we're getting an error. Let's open up our console and see what's happening. The server responded with a status of 419. That usually means that Laravel's validation broke, so let's open up this Network tab and try the upload again.
That usually means that Laravel's validation broke, so let's open up this Network tab and try the upload again. Page expired. Okay, that usually means that Laravel is expecting a CSRF token and one isn't being passed through in the headers. Normally we'd use a CSRF field in the form, but because this is a JavaScript plugin, we'll have to include it in a different way. Now going back to our source code, we could bypass this pretty easily by going to the VerifyCsrfToken middleware and adding in our upload route to the $except property. But let's say that we want to keep this CSRF validation for some additional security.
VerifyCSRFToken middleware and adding in our upload route to the AcceptArray property. But let's say that we want to keep this CSRF validation for some additional security. We can modify our FilePond setup to include it. So let's open up our app.vue file. And where we're importing VueFilePond, we're also going to import a method called setOptions. This is used, as you might guess, to set some global options for the FilePond component. And we can use it by calling setOptions, and it expects an object. Now this object can consist of multiple properties, but we're only really focusing on one. We're going to name that server, which is an object, and process as its property, which is also another object.
We're going to name that a server, which is an object, and process as its property, which is also another object. We're going to set two properties in this, the URL, and this will replace the URL that we have up here, /upload. So we can remove that, and call ./upload, because it's a relative URL. After that, we're calling a headers property, which is also an object. And this takes a key value pair, the keys being the properties of the headers that we're setting, and the values being their respective value. For Laravel, it's expecting an X-CSRF-Token header with the CSRF token. And we're going to be including that in our application as a meta tag, so we can query.
For Laravel, it's expecting an X-CSRF token header with the CSRF token. And we're going to be including that in our application as a meta tag, so we can query it with vanilla JavaScript by using document, head, querySelector, meta, name equals, csrftoken, and the content property for it. Alright so now each time that our application is loaded up, and each time that a file is uploaded, this X-CSRF token header will be present in the upload. We'll have to add that meta tag to our main Blade view, so let's open up our index.blade.php file, and add meta name, csrftoken, with the content just echoing out the CSRF token helper method. Alright I think it's time to try this out, but first because we made some modifications
method. Alright I think it's time to try this out, but first because we made some modifications to the app.vue file, we'll have to recompile it. Open our terminal, let's run npm run dev, and take a look in our browser. Alright so far so good, let's find an image to upload. And upload complete! Now I didn't have our console window open so I can't see what the network resolved back to it, but let's upload another image and see what we get back. Alright upload returned successfully, and we see our image object returned as expected. So we have the name of the image, the extension, and the size, as well as the ID and created
Alright upload returned successfully, and we see our Image object returned as expected. So we have the name of the image, the extension, and the size, as well as the ID and created_at dates. Now okay what if I wanted to visit this image? So let's copy this name, and remember that I put it under a public images directory, so I should expect to see it under /images, with the name of the image. Except I'm getting a 404 Not Found. That's pretty common in Laravel if you haven't set up the storage link. You see if we go back to our source code, if we go to the storage/app/public/images directory, our images are in here.
Expose Storage via Symlink10:13
You see if we go back to our source code, if we go to the storage/app/public/images directory, our images are in here. See here's the one that I just uploaded. But the problem is that this storage folder and everything underneath of it is not accessible to the browser because the application's root directory is the public folder up here. So what we need to do is create a symlink between something inside of the public directory and something inside of this storage directory here, specifically our public/images folder. Now luckily Laravel provides a handy artisan command to do just that. So if we open up our terminal, we can run php artisan storage:link, and our public/storage folder has been connected to a storage/app/public folder.
So if we open up our terminal, we can run php artisan storage:link, and our public storage folder has been connected to a storage/app/public folder. So now in public we have a storage folder that symlinks to storage/app/public. So now if we go back to our browser, we shouldn't see the image under just images with the image name. That's my fault. We should see it under storage/images. And there we go. Our image that we uploaded can be seen here at a public URL. In the next video, we're going to take those uploaded images and show them on the front.
Our image that we uploaded can be seen here at a public URL. In the next video, we're going to take those uploaded images and show them on the front end view in a little gallery display.
