Laravel Image Upload Made Easy

Laravel Image Upload Made Easy

Today, I will show you guys how to add Laravel Image Upload functionality into your application with validation. This one will be a step by step guide, by the end of this post you will familiarize how to upload an image in Laravel.

Uploading the image in Laravel is very easy! So if you are beginners then you can do that simply. Laravel provides a very simple way to create file uploading with proper validation like max file size 2mb, file extension should be jpeg, png, jpg, gif or SVG etc.

In this post, I will walk you through how we can add the profile image to Laravel’s default authentication which comes out of the box.

By the end of this post, you will have a working example like below.

Laravel Image Upload Made Easy
Laravel Image Upload Made Easy

So let’s start.

Application Setup

I assume, you already have fresh Laravel project, if not go and create with below command.

composer create-project laravel/laravel LaravelImageUpload

Now, open .env file which is located int the root of your LaravelImageUpload project folder. Update the database credentials with your ones.

Once you have connected a database to Laravel, run below command to generate default Laravel’s authentication scaffolding.

php artisan make:auth

Once you have generated the scaffold, we are ready to start.

Updating Migration File

Laravel provides you a User model and migration for this model by default. Go to database/migrations folder and open 2014_10_12_000000_create_users_table file.

We will be uploading a profile image for currently authenticated user, so we need to add an extra field in which we will store the image file path. Update the up() method with below one:

public function up()
    {
        Schema::create('users', function (Blueprint $table) {
            $table->increments('id');
            $table->string('name');
            $table->string('email')->unique();
            $table->string('profile_image')->nullable(); // our profile image field
            $table->timestamp('email_verified_at')->nullable();
            $table->string('password');
            $table->rememberToken();
            $table->timestamps();
        });
    }

As you have noticed that, we have added a new field called profile_image and set its type to string. Notice that we have added a nullable() flag to new fields to keep this image uploading optional for users.

Once you have updated your migration, run below command to create all your migrations.

php artisan migrate

Now if you, check your database you will be able to see that Laravel will create two tables called users and password_resets.

Now it’s time to update our User mode. Open app/User.php file and add profile_image to $fillable array.

protected $fillable = ['name', 'email', 'password', 'profile_image'];

Also we will add a new accessor method to get the user’s profile image like so auth()->user()->image instead of using profile_image field. So add below code within User class.

public function getImageAttribute()
{
   return $this->profile_image;
}

Profile Routes

After migrating tables and updating User model, now we need to setup a new page with form where we will be able to upload image. So for that create two routes in your web.php routes file. Like so:

Route::get('/profile', 'ProfileController@index')->name('profile');
Route::post('/profile/update', 'ProfileController@updateProfile')->name('profile.update');

Profile Controller

As you have noticed that we have used ProfileController in above routes, so run below command to create a new controller.

php artisan make:controller ProfileController

Now open your newly created controller and update with below code:

namespace App\Http\Controllers;

use Illuminate\Http\Request;

class ProfileController extends Controller
{
    public function __construct()
    {
        $this->middleware('auth');
    }

    public function index()
    {
        return view('auth.profile');
    }
}

As you have seen in the __construct() method we have set up the middleware so only authenticated users will be able to update their profile.

In index() method we are just loading a new view called profile which is residing inside auth folder generated by Laravel.

Profile Blade Templates

Above code example is just bootstrap template with a form to upload an image. In form’s action we have added {{ route('profile.update') }} route, so when we will submit this form it will hit that route.

Note that we have added enctype in the form declaration which very important for uploading files. Without this your form will not upload any file.

Next, we have a input field for user name and loading it’s current value using authentication helper like this auth()->user()->name.

Next, we have a input field for user email and loading its value in value attribute. I’m keeping this field disabled so that email change is not possible.

Now, we need to add a drop-down menu for the currently authenticated user to access the profile page. For that in your resources/views/layouts/app.blade.php file add below line of code just before the Logout dropdown item.

<a class="dropdown-item" href="{{ route('profile') }}">Profile</a>

Now if you click on the Profile link it will load the below view.

Profile Page
Profile Page

Configuring FileSystem for Storage

After finishing our view templates modification, now we need to configure our file system where we will be uploading images. Go to config/filesystem.php file and change the settings for public disk with below.

'public' => [
            'driver' => 'local',
            'root' => public_path(),
            'url' => env('APP_URL').'/public',
            'visibility' => 'public',
        ],

Image Upload Trait

You can handle the image uploading logic within your controller, but I would like to separate this logic into a trait which we can use later on if we need to upload images.

In your app folder, create folder called Traits and add a php file called UploadTrait.php. Add below code snippet in it.

namespace App\Traits;

use Illuminate\Support\Str;
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Facades\Storage;

trait UploadTrait
{
    public function uploadOne(UploadedFile $uploadedFile, $folder = null, $disk = 'public', $filename = null)
    {
        $name = !is_null($filename) ? $filename : Str::random(25);

        $file = $uploadedFile->storeAs($folder, $name.'.'.$uploadedFile->getClientOriginalExtension(), $disk);

        return $file;
    }
}

In the above code example, we are creating a new function called uploadOne which will handle the file uploading by taking the uploaded image, folder, disk, and filename parameters.

Firstly, we are checking if a filename has been passed, if not then we are creating a random string name.

Next we are uploading the file using UploadedFile‘s storeAs method and returning the file we just stored. Nothing fancy.

Processing Image Upload in Profile Controller

After setting up the filesystem and creating a trait, it’s the time now to use the trait in our ProfileController for handling the form submission. As we already have created a route which will hit the updateProfile method. So let’s add that method. Use the below code example, and replace your current ProfileController with this one.

namespace App\Http\Controllers;

use App\User;
use Illuminate\Http\Request;
use App\Traits\UploadTrait;

class ProfileController extends Controller
{
    use UploadTrait;

    public function __construct()
    {
        $this->middleware('auth');
    }

    public function index()
    {
        return view('auth.profile');
    }

    public function updateProfile(Request $request)
    {
        // Form validation
        $request->validate([
            'name'              =>  'required',
            'profile_image'     =>  'required|image|mimes:jpeg,png,jpg,gif|max:2048'
        ]);

        // Get current user
        $user = User::findOrFail(auth()->user()->id);
        // Set user name
        $user->name = $request->input('name');

        // Check if a profile image has been uploaded
        if ($request->has('profile_image')) {
            // Get image file
            $image = $request->file('profile_image');
            // Make a image name based on user name and current timestamp
            $name = Str::slug($request->input('name')).'_'.time();
            // Define folder path
            $folder = '/uploads/images/';
            // Make a file path where image will be stored [ folder path + file name + file extension]
            $filePath = $folder . $name. '.' . $image->getClientOriginalExtension();
            // Upload image
            $this->uploadOne($image, $folder, 'public', $name);
            // Set user profile image path in database to filePath
            $user->profile_image = $filePath;
        }
        // Persist user record to database
        $user->save();

        // Return user back and show a flash message
        return redirect()->back()->with(['status' => 'Profile updated successfully.']);
    }
}

Above code is self-explained, we have injected our trait into this controller and added a updateProfile method.

Go thorugh the updateProfile method and read the comments to follow along. I have added enough comments to explain every single line.

After updating the ProfileController, if you now go to the profile page and submit an image it should be working as expected. Try to upload a wrong file type and you will get the validation errors displayed as well like below.

Image Upload Validation
Image Upload Validation
Image Upload Validation
Image Upload Validation

Adding Profile Image to Dropdown Menu

By now you should have a fully functional image upload profile section. One last thing we need to show the currently uploaded image next to the username in header like in the demo image. For that in your app.blade.php layout file, replace this:

<a id="navbarDropdown" class="nav-link dropdown-toggle" href="#" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" v-pre>
        {{ Auth::user()->name }} <span class="caret"></span>
    </a>

with this:

<a id="navbarDropdown" class="nav-link dropdown-toggle" href="#" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" v-pre>
        @if (auth()->user()->image)
            <img src="{{ asset(auth()->user()->image) }}" style="width: 40px; height: 40px; border-radius: 50%;">
        @endif
        {{ Auth::user()->name }} <span class="caret"></span>
    </a>

Now if you upload an image again, you will be able to see the uploaded image before the user name.

Great! we have successfully created a profile update section with Laravel Image Upload. You can find the Source Code of the above example on Github.

If you have any question or suggestion to improve this post, please let me know in the comments box below.

25 comments on “Laravel Image Upload Made Easy

    1. Hi,
      You can create a deleteOne method in the trait with following code.

      public function deleteOne($folder = null, $disk = 'public', $filename = null)
      {
         Storage::disk($disk)->delete($folder.$filename);
      }

      Change the $disk value to whatever you are using.

    1. Hi,

      Images are stored in the public folder in this post and a path to the image will be stored in the database table. Then you can call the image path like below.

      {{ asset(auth()->user()->image) }}

      Thanks

  1. Hello there thanks for this awesome post. It really helped me with my project. But I also have another problem on my project about relationships that’s is showing images that belongs to a certain post. I just wanted to know if anyone can help me out with it if I posted it here. Thanks. Zeus.

  2. Hello am new to laravel, though my question may sound stupid but please someone can still help out. I want to know if I can use the same steps as stated above for image upload to upload a zip file??

  3. Yo Ssup. Thanks for this. Before i was copying and pasting my upload code all over, and now with this i can easily reuse it because of the trait. 🙂 Now that i think about it its funny how i have been coding for a while but never thought of using traits.

  4. Symfony\Component\Debug\Exception\FatalThrowableError
    Call to undefined function App\Http\Controllers\str_slug()

    error uploading image, cannot proceed

  5. hi,
    it just awesome post but i wanna know a little about of that, will this work same if i am trying to upload image from a bootstrap modal in laravel.

Leave a Reply

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

*

When sharing a code snippet please wrap you code with pre tag and add a class code-block to it like below.
<pre class="code-block">you code here</pre>

*
*

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

Your little help will keep this site alive and help us to produce quality content for you.