Laravel 8 Image File Upload With Inertia Js  Vue 3 Example

In this tutorial, we will see how to upload image file with validation using laravel inertia js in vue 3. For this section we will use laravel 8 with laravel breeze, laravel inertia js vue 3, laravel File Storage for image upload. let see.


Table of Contents

Step 1: Create Laravel Project & Connect Database

Step 2: Install Breeze & Setup Inertia Js Vue 3

Step 3: Create Model, Migration and Controller

Setp 4: Create Image Request for Validation

Step 5: Define Image routes

Step 6: Create a view file



Step 1: Create Laravel Project & Connect Database

Installing a fresh new laravel application.

composer create-project --prefer-dist laravel/laravel inertia-file-upload

Now, you have to connect the laravel app to the database, hence open the .env configuration file and add the database credentials as suggested below.

.env

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=database_name
DB_USERNAME=database_user_name
DB_PASSWORD=database_password



Step 2: Install Breeze & Setup Inertia Js Vue 3

Install laravel breeze via composer

composer require laravel/breeze --dev

Next, run below command

php artisan breeze:install

install breeze with vue 3

php artisan breeze:install vue

And final install Dependencies

npm install && npm run dev 


To create the symbolic link, you may use the storage:link Artisan command:

php artisan storage:link



Step 3: Create Model, Migration and Controller

Create a Model, Migration and Controller

php artisan make:model Image -mc 


Model created successfully.
Created Migration: 2021_09_15_115013_create_images_table
Controller created successfully


Add field in migration

<?php

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

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

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


Image Model

app/Models/Image.php

<?php

namespace App\Models;

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

class Image extends Model
{
    protected $fillable = ['image'];
    
    use HasFactory;
}


Setp 4: Create Image Request for Validation

To Create Image request run below command.

php artisan make:request StoreImage 

app/Http/Requests/StoreImage.php

<?php

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;

class StoreImage extends FormRequest
{
    /**
     * Determine if the user is authorized to make this request.
     *
     * @return bool
     */
    public function authorize()
    {
        return true;
    }

    /**
     * Get the validation rules that apply to the request.
     *
     * @return array
     */
    public function rules()
    {
        return [
            'image' => 'required|image|mimes:jpeg,jpg,png,gif,svg|max:2048'
        ];
    }
}


Image Controller

app/Http/Controllers/ImageController.php

<?php

namespace App\Http\Controllers;

use App\Http\Requests\StoreImage;
use App\Models\Image;
use Illuminate\Support\Facades\Redirect;
use Illuminate\Support\Facades\Request;
use Inertia\Inertia;
use Illuminate\Support\Facades\Storage;


class ImageController extends Controller
{
    public function index()
    {
        $images = Image::latest()->get();

        return Inertia::render('Image/Index', ['images' => $images]);
    }

    public function create()
    {
        return Inertia::render('Image/Create');
    }

    public function store(StoreImage $request)
    {

    $image_path = '';

    if ($request->hasFile('image')) {
        $image_path = $request->file('image')->store('image', 'public');
    }

    $data = Image::create([
        'image' => $image_path,
    ]);


        return Redirect::route('image.index');
    }

}


Step 5: Define Image routes

Create Routes

web.php

<?php

use App\Http\Controllers\ImageController;
use Illuminate\Foundation\Application;
use Illuminate\Support\Facades\Route;
use Inertia\Inertia;

/*
|--------------------------------------------------------------------------
| Web Routes
|--------------------------------------------------------------------------
|
| Here is where you can register web routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| contains the "web" middleware group. Now create something great!
|
*/

Route::get('/', function () {
    return Inertia::render('Welcome', [
        'canLogin' => Route::has('login'),
        'canRegister' => Route::has('register'),
        'laravelVersion' => Application::VERSION,
        'phpVersion' => PHP_VERSION,
    ]);
});

Route::get('/dashboard', function () {
    return Inertia::render('Dashboard');

})->middleware(['auth', 'verified'])->name('dashboard');
Route::get('image', [ImageController::class,'index'])->name('image.index');
Route::get('image/create', [ImageController::class,'create'])->name('image.create');
Route::post('image', [ImageController::class,'store'])->name('image.store');
require __DIR__.'/auth.php';


Step 6: Create a view file

app/resources/js/Pages/Image/Create.vue

<template>
    <Head title="Dashboard" />

    <BreezeAuthenticatedLayout>
        <template #header>
            <h2 class="text-xl font-semibold leading-tight text-gray-800">
                Image Create
            </h2>
        </template>

        <div class="py-12">
            <div class="mx-auto max-w-7xl sm:px-6 lg:px-8">
                <div class="overflow-hidden bg-white shadow-sm sm:rounded-lg">
                    <div class="p-6 bg-white border-b border-gray-200">
                        <form @submit.prevent="submit">
                            <div>
                                <label for="File">File Upload</label>
                                <input
                                    type="file"
                                    @change="previewImage"
                                    ref="photo"
                                    class="
                                        w-full
                                        px-4
                                        py-2
                                        mt-2
                                        border
                                        rounded-md
                                        focus:outline-none
                                        focus:ring-1
                                        focus:ring-blue-600
                                    "
                                />
                                <img
                                    v-if="url"
                                    :src="url"
                                    class="w-full mt-4 h-80"
                                />
                                <div
                                    v-if="errors.image"
                                    class="font-bold text-red-600"
                                >
                                    {{ errors.image }}
                                </div>
                            </div>

                            <div class="flex items-center mt-4">
                                <button
                                    class="
                                        px-6
                                        py-2
                                        text-white
                                        bg-gray-900
                                        rounded
                                    "
                                >
                                    Save
                                </button>
                            </div>
                        </form>
                    </div>
                </div>
            </div>
        </div>
    </BreezeAuthenticatedLayout>
</template>

<script>
import BreezeAuthenticatedLayout from "@/Layouts/Authenticated.vue";
import BreezeLabel from "@/Components/Label";
import { Head } from "@inertiajs/inertia-vue3";
import { useForm } from "@inertiajs/inertia-vue3";
export default {
    components: {
        BreezeAuthenticatedLayout,
        Head,
    },
    props: {
        errors: Object,
    },
    data() {
        return {
            url: null,
        };
    },
    setup() {
        const form = useForm({
            image: null,
        });

        return { form };
    },
    methods: {
        submit() {
            if (this.$refs.photo) {
                this.form.image = this.$refs.photo.files[0];
            }
            this.form.post(route("image.store"));
        },
        previewImage(e) {
            const file = e.target.files[0];
            this.url = URL.createObjectURL(file);
        },
    },
};
</script>


Validation Image


Preview Image Before Upload



app/resources/js/Pages/Image/Index.vue

<template>
    <Head title="Dashboard" />

    <BreezeAuthenticatedLayout>
        <template #header>
            <h2 class="text-xl font-semibold leading-tight text-gray-800">
                Image Index
            </h2>
        </template>

        <div class="py-12">
            <div class="max-w-3xl mx-auto sm:px-6 lg:px-8">
                <div class="overflow-hidden bg-white shadow-sm sm:rounded-lg">
                    <div class="p-6 bg-white border-b border-gray-200">
                        <div class="mb-4">
                            <Link
                                class="
                                    px-6
                                    py-2
                                    mb-2
                                    text-green-100
                                    bg-green-500
                                    rounded
                                "
                                :href="route('image.create')"
                            >
                                Create
                            </Link>
                        </div>
                        <table class="w-full">
                            <thead class="font-bold bg-gray-300 border-b-2">
                                <td class="px-4 py-2">ID</td>
                                <td class="px-4 py-2">image</td>
                                <td class="px-4 py-2">Action</td>
                            </thead>
                            <tbody>
                                <tr v-for="image in images" :key="image.id">
                                    <td class="px-4 py-2">{{ image.id }}</td>
                                    <td class="px-4 py-2">
                                        <img
                                            :src="showImage() + image.image"
                                            class="object-cover h-40 w-80"
                                        />
                                    </td>
                                    <td class="px-4 py-2">Action</td>
                                </tr>
                            </tbody>
                        </table>
                    </div>
                </div>
            </div>
        </div>
    </BreezeAuthenticatedLayout>
</template>

<script>
import BreezeAuthenticatedLayout from "@/Layouts/Authenticated.vue";
import BreezeNavLink from "@/Components/NavLink.vue";
import { Head } from "@inertiajs/inertia-vue3";
import { Link } from "@inertiajs/inertia-vue3";
export default {
    components: {
        BreezeAuthenticatedLayout,
        Head,
        BreezeNavLink,
        Link,
    },
    methods: {
        showImage() {
            return "/storage/";
        },
    },

    props: {
        images: Object,
    },
};
</script>




Note: Always try to open to terminal window one use for server and second for below command.

npm run watch