Image Uploads Using Laravel 5.4 With Vue: Part 2

/ PHP / by Paul Robinson / 0 comments
This post was published back on May 2, 2017 and may be outdated. Please use caution when following older tutorials or using older code. After reading be sure to check for newer procedures or updates to code.

When we last left our image upload application in part 1 we had created a working image uploader, but it could still be improved. In this second part we are going to look at improving the application by adding in thumbnailing and moving our image listing into a separate page.

Vue Router

To start off we will install Vue Router. In your terminal, while in your Laravel project root, use NPM to install Vue Router:

We are using --save-dev to make sure it is saved as a dependency in our project. Now let’s get our Router working.

To use Vue Router we need to re-organise our app.js file a little while adding some new code. Let’s do that now.

There are a few changes here. We have imported VueRouter, we have told Vue that the router plugin exists using the Vue.use() function.

Note the backticks on the second component. This allows interpolation of variables inside strings. These are called Template Strings and are usable only because we are using ES2015.

Next we create two components that we can pass to our routes object. We create our routes object passing in a component to the relevant route. We then pass that routes object to VueRouter, note that just passing the routes object is shorthand for routes: routes. Finally we pass the router object to Vue and mount the app again.

Don’t forget to recompile your Javascript by using.

Next we need to adjust our blade template. I will show all the code inside the body tag to help with brevity.

Here we’ve moved our code into a container for the Vue mount and added in a navigation bar. We are just using Bootstrap’s since we have it included anyway. You can see we also have a new tag router-link this creates a hyperlink that goes to a route rather than a different web page. The extra parameters are as follows:

  • to: which route path to link too
  • tag: which html tag should be used when creating the link. We are using li here as that is the tag bootstrap applies its active class too
  • active-class: what class should be used when the link is active
  • exact: should the route have to be matched exactly to be classed as active

The a tag inside is recognised by Vue and is used to create the real hyperlink.

The final part is the router-view tag. This is where Vue Router will place the content of the current route. You can have more than one and they can be named, but we are keeping it simple here and just using a single default tag.

Changes To Image Listing

Now we are going to adjust our image listing to suit being on its own page and to be ready for adding in thumbnails.

The HTML has changed a fair bit, we’ve basically added a heading and some conditionals. First is a loader, you can replace the text with a loading graphic such as one from Next we have a check for files and a message to show if no images could be found.

The JavaScript code hasn’t changed too much. We have added watch which allows us to run a certain method when the route is activated. In this case we just run the refresh method to make sure our images are up-to-date. A more robust system that checks to see if the images need to be refreshed might be called for here, but this is fine for the purposes of this tutorial. We also add the loading variable to our data and do a simple true/false switch during the refresh method to turn it on and off.

Laravel Models

If you haven’t already open up your .env file and enter your MySQL connection details.

Now we can move on to our Laravel code. First we are going to change to using a database as our source for the images as opposed to just listing them from the directory like in the first part of this tutorial. So let’s create our model. We are going to use Artisan so if you are using Docker or a VM you’ll need to login to that to use it.

That will generate a model and a migration. First let’s open up the migration file it should be located in /database/migrations. You will want to add a path field and a thumb field your Schema block might look like this.

Now we can run our migration. Back with artisan.

This will run the migration and create the table. Next we can open up the Model which should be located in the /app folder. This file will be pretty simple.

We add in a $fillable property to allow use to mass assign both path and thumb. We also define two accessors to adjust the path we have saved in the database. These adjust the path after retrieving them, the data in the table remains untouched. The saved path will be relative and the storage folder will be missing from the path laravel returns due to it being a symbolically linked public folder. I’m unsure if this is expected behaviour of Laravel but it is something I came across consistently and so worked around it like this.

Main Controller Adjustments

Now we can adjust our main controller to thumbnail files on upload. First though we need something to do the thumbnailing as Laravel doesn’t do it out-of-the-box. With that in mind we are going to pull the excellent Intervention Image library to do the work for us. To install please follow the instructions on the Intervention website as it is written out on there perfectly. There is however one caveat. Since our model is named Image you will want to rename the facade for Intervention to something else. I used InterImage.

Once installed we can adjust our controller.

We’ve changed the code a bit here. First note all of the namespaces we have imported at the top. Again InterImage is the name I used for the Intervention Image facade if you chose something different you’ll need to change that to whatever you chose in the code above.

First up we are adding some file validation using the validate method that is automatically included by Laravel on any Controller that extends the base Controller. We still check the file is valid even after validation, just in case.

Now we store our image using the putFile method of the Storage facade. We are doing this so that we get a valid path back that we can re-use in the Storage facade. We then make a thumbnail using Intervention by passing through the image we just stored, the output is stored in a variable as a stream. We then create thumbnail path, for simplicity we are storing them in the same folder with a random name. Then we use the Storage facade’s put method to output the thumbnail as a file.

Once all that is complete we add the image and thumbnail paths to the database. We finally return a success or failure message based on the result.

One final note is the output of the list method has changed to Image::all() as we are now returning the results from a Database. Returning the method like this automatically returns a JSON object thanks to Eloquent.


You thought you left homework at school? Think again, here are some things you could add if you are feeling up to it.

Image Cleanup

If there is a failure after the image and thumbnail have been saved they will be left in the storage folder but never used due to their entry being missing from the database. Try to adjust the code to clean up if there is a catchable error after the images have been saved/created.

Image Naming

The images are named using a random string, Laravel uses this method for its store method if you save the file directly from the request’s file object. However maybe they could be named in a better way more suitable for your application.

Image List Refresh

While loading a JSON response is efficient maybe there is a way to determine if the image list needs to be refreshed when the route is changed?

That’s It

We have finished. It’s been a long journey but that is finally it. You should now have a working uploader. It is fairly simple and there are a lot of improvements that can be made, but hopefully this helps with the basics.

If you have any questions please let me know in the comments, I will try to answer as quickly as I can.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.

I'll keep your WordPress site up-to-date and working to its best.

Find out more