Posts Learn Components Snippets Categories Tags Tools About
/

Simple Image Upload with Validation in Laravel 8

Learn how to easily upload image in Laravel 8 with proper validation and error feedback

2 years ago

10 mins read

4126 views

In this post, you'll learn how to implement image upload with proper validation and error feedback messages in Laravel 8. The steps are very simple to follow and understand so let's get started.
I assume you will integrate the image upload into your existing Laravel 8 application, if you are just getting started then I do recommend following the Laravel installation guide from the documentation. With that out of the way, let's start by creating the model and migration.

Step 1: Create Model and Migration


First, let's make a model with its corresponding migration file to store the path of the uploaded image. Run the command below to generate the model and migration.
php artisan make:model ImageUpload -m
Do note that the command above has the -m flag which will create the migration as well. Inside the migration class, define a new column that's a string type and call it "image_url".
<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

class CreateImageUploadsTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('image_uploads', function (Blueprint $table) {
            $table->id();
            $table->string('image_url');
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('image_uploads');
    }
}

Inside of the ImageUpload model, do specify the fillable field so that the attribute is mass assignable.
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Factories\HasFactory;

class ImageUpload extends Model
{
    use HasFactory;

    /**
     * The attributes that are mass assignable.
     *
     * @var array
     */
    protected $fillable = [
        'image_url'
    ];
}

Step 2: Define Route


The route will be the path where the user can access the page, for this tutorial the path will be called "/image-upload" and it accepts both "get" and "post" method.
/* allow the route to accept 'get' and 'post' request */
Route::match(['get', 'post'], '/image-upload', function () {
        /* upload code goes here later */

        return view('image-upload');
    })
    ->name('image-upload');;
The method should return a view with the name of "image-upload". The page should contain as follows.
<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <meta name="csrf-token" content="{{ csrf_token() }}">
        <title>{{ config('app.name', 'Laravel Example') }}</title>
        <link href="https://unpkg.com/tailwindcss@^2/dist/tailwind.min.css" rel="stylesheet">
    </head>
    <body class="flex items-center justify-center w-full h-screen">
        <div class="flex flex-col items-center justify-center max-w-xs">
            <form
                action="{{ route('image-upload') }}"
                method="post"
                enctype="multipart/form-data"
                class="w-full bg-gray-100 rounded-lg h-32 p-4"
            >
                @csrf
                <input
                    type="file"
                    name="image-upload"
                >

                @error('image-upload')
                    <p class="text-red-500">
                        {{ $message }}
                    </p>
                @enderror

                <button
                    type="submit"
                    class="mt-4 px-4 py-2 rounded bg-green-200 font-medium text-green-700"
                >
                    Upload Image
                </button>
            </form>

            <div class="mt-4 w-full">
                @foreach($images as $image)
                    <img
                        src="{{ \Illuminate\Support\Facades\Storage::disk('public')->url($image->image_url) }}"
                        class="w-full h-auto"
                        alt="Image uploads"
                    >
                @endforeach
            </div>
        </div>
    </body>
</html>
The page styling is using TailwindCSS and do make sure that the file input is wrapped within a form. It also comes with the code to output an error message to the user.
@error('image-upload')
    <p class="text-red-500">
        {{ $message }}
    </p>
@enderror
In addition to that, the loop that will load all the images is also included and it's like the code below.
<div class="mt-4 w-full">
    @foreach($images as $image)
        <img
            src="{{ \Illuminate\Support\Facades\Storage::disk('public')->url($image->image_url) }}"
            class="w-full h-auto"
            alt="Image uploads"
        >
    @endforeach
</div>

Step 3: Preview of the Image Upload Page


With all of the TailwindCSS classes, the image upload page design will be like below. Although it looks simple, it serve the purpose of this example.
Image Upload Page Preview

Step 4: Save Image and Validation Logic


Now let's revisit the route and update the code to save the image. The full code will look like below.
use App\Models\ImageUpload;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;

/* allow the route to accept 'get' and 'post' request */
Route::match(['get', 'post'], '/image-upload', function (Request $request) {
    /* check if there's file upload */
    if ($request->hasFile('image-upload')) {
        /* validate the request to check if there's any image upload */
        $validatedData = $request->validate([
            'image-upload' => 'required|image|mimes:jpeg,png,jpg|max:1024',
        ]);

        /* store the image in the 'uploads' directory */
        $path = $validatedData['image-upload']->store('uploads');

        /* save the image path into the db */
        ImageUpload::create(['image_url' => $path]);
    }

    /* retrieve existing uploaded images*/
    $images = ImageUpload::all();

    /* return the view to front-end and attach any existing images if available */
    return view('image-upload', compact('images'));
})->name('image-upload');
To check if there's an image uploaded make use of the "hasFile" method and once it passes, do validate the file by checking the request with the "validate" method. If the validation encounters an error the page will be redirected back to this page and the error message will be passed on to the view.
/* check if there's file upload */
if ($request->hasFile('image-upload')) {
    /* validate the request to check if there's any image upload */
    $validatedData = $request->validate([
        'image-upload' => 'required|image|mimes:jpeg,png,jpg|max:1024',
    ]);

    /* store the image in the 'uploads' directory */
    $path = $validatedData['image-upload']->store('uploads');

    /* save the image path into the db */
    ImageUpload::create(['image_url' => $path]);
}

To store the image directly call the "store" method from the validated input. and then finally if it's successfully saved to the storage, save the image URL path to the database.
/* store the image in the 'uploads' directory */
$path = $validatedData['image-upload']->store('uploads');

/* save the image path into the db */
ImageUpload::create(['image_url' => $path]);
Do note that this logic only triggered during the "POST" request but when it's a "GET" request, it won' the triggered. For the "GET" request all the image upload is retrieved from the DB and it's right away passed onto the views.
/* retrieve existing uploaded images*/
$images = ImageUpload::all();

Step 5: Environment Variable and Storage Linking


Do note that you have to update the .env file to set the filesystem driver to "public".
FILESYSTEM_DRIVER=public
Aside from that, you will have to run the command to link the storage directory to the public directory.
php artisan storage:link

Step 6: Image Successfully Uploaded


Below is the preview upon successfully uploading an image.
Image successfully uploaded and displayed on the page

On the second attempt if you are uploading different files that don't pass the validation, then it will look like below.
Image upload return error after non-image file is uploaded

Alternative Tags

If you like our tutorial, do make sure to support us by being our Patreon or buy us some coffee ☕️

)