In this tutorial, we’ll explore how to upload an image file with validation using Laravel Inertia.js in Vue 3. For this section, we’ll use Laravel 8 with Laravel Breeze, Laravel Inertia.js Vue 3, and Laravel File Storage for image upload. Let’s dive in.
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.
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
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');
}
}
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'
];
}
}
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
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
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 two terminal windows – one for the server and the other for executing the commands below.
npm run dev