Image Upload Tutorial with 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

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 authfolder generated by Laravel.

Profile Blade Templates

@extends('layouts.app')
	

	@section('content')
	    <div class="container">
	        <div class="row justify-content-center">
	            <div class="col-md-12">
	                <div class="card">
	                    <div class="card-header">Profile</div>
	                    <div class="card-body">
	                        @if (session('status'))
	                            <div class="alert alert-success" role="alert">
	                                {{ session('status') }}
	                            </div>
	                        @endif
	                        <div class="container">
	                            <div class="row">
	                                <div class="col-12">
	                                    @if ($errors->any())
	                                        <div class="alert alert-danger alert-dismissible" role="alert">
	                                            <button type="button" class="close" data-dismiss="alert" aria-label="Close">
	                                                <span aria-hidden="true">×</span>
	                                            </button>
	                                            <ul>
	                                                @foreach ($errors->all() as $error)
	                                                    <li>
	                                                        {{ $error }}
	                                                    </li>
	                                                @endforeach
	                                            </ul>
	                                        </div>
	                                    @endif
	                                    <form action="{{ route('profile.update') }}" method="POST" role="form" enctype="multipart/form-data">
	                                        @csrf
	                                        <div class="form-group row">
	                                            <label for="name" class="col-md-4 col-form-label text-md-right">Name</label>
	                                            <div class="col-md-6">
	                                                <input id="name" type="text" class="form-control" name="name" value="{{ old('name', auth()->user()->name) }}">
	                                            </div>
	                                        </div>
	                                        <div class="form-group row">
	                                            <label for="email" class="col-md-4 col-form-label text-md-right">Email</label>
	                                            <div class="col-md-6">
	                                                <input id="email" type="text" class="form-control" name="email" value="{{ old('email', auth()->user()->email) }}" disabled>
	                                            </div>
	                                        </div>
	                                        <div class="form-group row">
	                                            <label for="profile_image" class="col-md-4 col-form-label text-md-right">Profile Image</label>
	                                            <div class="col-md-6">
	                                                <input id="profile_image" type="file" class="form-control" name="profile_image">
	                                                @if (auth()->user()->image)
	                                                    <code>{{ auth()->user()->image }}</code>
	                                                @endif
	                                            </div>
	                                        </div>
	                                        <div class="form-group row mb-0 mt-5">
	                                            <div class="col-md-8 offset-md-4">
	                                                <button type="submit" class="btn btn-primary">Update Profile</button>
	                                            </div>
	                                        </div>
	                                    </form>
	                                </div>
	                            </div>
	                        </div>
	                    </div>
	                </div>
	            </div>
	        </div>
	    </div>
	@endsection

profile.blade.php

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.

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

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\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.

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

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.

Recommended Reading

Laravel Performance Optimization: Guide to a Perfect Laravel Developer

How to remove multiple keys from PHP Array?

Laravel Web Tinker

Learn how to create your first Laravel package

Laravel 5.8 Tutorial - Datatables Dropdown Filter Server-side using Ajax

How to Install Laravel Local Dev Environment On Ubuntu

Laravel Eager Loading Tutorial

How to resize image in Laravel 5.8 App

#laravel #php

Image Upload Tutorial with Laravel
157.85 GEEK