In this section, we will learn how to upload images in Laravel using the Laravel Intervention package. Using Laravel Intervention Image, we can resize images before uploading. In this tutorial, we will create a blog CRUD with image upload functionality using Laravel Intervention Image. We will also see how to upload, edit, resize, and delete images with both the database and the folder. For this tutorial, we will use Laravel Breeze and Tailwind CSS.
Step 1: Install Laravel & Connect Database
Run below command to create laravel project.
composer create-project laravel/laravel laravel-intervention
Now, you need to connect the Laravel app to the database. 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
Install laravel breeze via composer:
composer require laravel/breeze --dev
Next, run below command.
php artisan breeze:install
And final install Dependencies.
npm install && npm run dev
php artisan migrate
Step 3: Create Blog Modal Migration and Controller
Run below command to create model, migration and controller.
php artisan make:model Blog -mcr
create_blogs_table.php
public function up()
{
Schema::create('blogs', function (Blueprint $table) {
$table->id();
$table->string('title');
$table->text('content');
$table->string('image')->nullable();
$table->timestamps();
});
}
app/Models/Blog.php
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Blog extends Model
{
use HasFactory;
protected $fillable = [
'title',
'content',
'image'
];
}
Create Routes for blog.
Route::resource('blogs', BlogController::class);
Step 4: install Intervention Image package
run below command to install.
composer require intervention/image
After installing Intervention Image, open your Laravel config file config/app.php
and add the following lines:
In the $providers
array, add the service providers for this package.
Intervention\Image\ImageServiceProvider::class
Add the facade of this package to the $aliases array.
'Image' => Intervention\Image\Facades\Image::class
Now the Image Class will be auto-loaded by Laravel.
Publish configuration in Laravel.
php artisan vendor:publish --provider="Intervention\Image\ImageServiceProviderLaravelRecent"
Next, you need to create a folder in the public path. You can name it something like uploads
, images
, or products
.
Step 5: Perform Blog CRUD with Image Upload Using Intervention Image
Next, import the Intervention Image and File classes in your controller to handle image uploads, edits, updates, and deletions using the public path.
app/Http/Controllers/BlogController.php
<?php
namespace App\Http\Controllers;
use App\Models\Blog;
use Illuminate\Http\Request;
use Image, File;
class BlogController extends Controller
{
/**
* Display a listing of the resource.
*
* @return \Illuminate\Http\Response
*/
public function index()
{
$blogs = Blog::all();
return view('blogs.index', compact('blogs'));
}
/**
* Show the form for creating a new resource.
*
* @return \Illuminate\Http\Response
*/
public function create()
{
return view('blogs.create');
}
/**
* Store a newly created resource in storage.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function store(Request $request)
{
$data = $request->validate([
'title' => 'required|string|max:255',
'content' => 'required',
'image' => 'nullable|image|mimes:jpg,png,jpeg,gif,svg|max:2048'
]);
$image_name = uniqid() . '.' . $data['image']->getClientOriginalExtension();
$image_path = 'uploads/' . $image_name;
Image::make($data['image'])->resize(320, 240)->save(public_path($image_path));
$data['image'] = $image_path;
$blog = Blog::create($data);
return redirect()->route('blogs.index');
}
/**
* Display the specified resource.
*
* @param \App\Models\Blog $blog
* @return \Illuminate\Http\Response
*/
public function show(Blog $blog)
{
return view('blogs.show', compact('blog'));
}
/**
* Show the form for editing the specified resource.
*
* @param \App\Models\Blog $blog
* @return \Illuminate\Http\Response
*/
public function edit(Blog $blog)
{
return view('blogs.edit', compact('blog'));
}
/**
* Update the specified resource in storage.
*
* @param \Illuminate\Http\Request $request
* @param \App\Models\Blog $blog
* @return \Illuminate\Http\Response
*/
public function update(Request $request, Blog $blog)
{
$data = $request->validate([
'title' => 'required|string|max:255',
'content' => 'required',
'image' => 'nullable|image|mimes:jpg,png,jpeg,gif,svg|max:2048'
]);
if ($request->has('image')) {
unlink(public_path($blog->image));
$image_name = uniqid() . '.' . $data['image']->getClientOriginalExtension();
$image_path = 'uploads/' . $image_name;
Image::make($data['image'])->resize(320, 240)->save(public_path($image_path));
$data['image'] = $image_path;
} else {
$data['image'] = $blog->image;
}
$blog->update($data);
return redirect()->route('blogs.index');
}
/**
* Remove the specified resource from storage.
*
* @param \App\Models\Blog $blog
* @return \Illuminate\Http\Response
*/
public function destroy(Blog $blog)
{
$image = $blog->image;
$blog->delete();
File::delete(public_path($image));
return redirect()->route('blogs.index');
}
}
Step 6: Create Blade View File
view/blogs/create.blade.php
<x-app-layout>
<x-slot name="header">
<h2 class="text-xl font-semibold leading-tight text-gray-800">
{{ __('Blogs Create') }}
</h2>
</x-slot>
<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 method="POST" action="{{ route('blogs.store') }}" enctype="multipart/form-data">
@csrf
<div class="mb-6">
<label class="block">
<span class="text-gray-700 @error('title') text-red-500 @enderror">Title</span>
<input type="text" name="title"
class="block @error('title') border-red-500 bg-red-100 text-red-900 @enderror w-full mt-1 rounded-md"
placeholder="" value="{{old('title')}}" />
</label>
@error('title')
<div class="flex items-center text-sm text-red-600">
{{ $message }}
</div>
@enderror
</div>
<div class="mb-6">
<label class="block">
<span class="text-gray-700 @error('content') text-red-500 @enderror">Content</span>
<textarea
class="block @error('content') border-red-500 bg-red-100 text-red-900 @enderror w-full mt-1 rounded-md"
name="content" rows="3">{{old('content')}}</textarea>
</label>
@error('content')
<div class="flex items-center text-sm text-red-600">
{{ $message }}
</div>
@enderror
</div>
<div class="mb-6">
<label class="block">
<span class="sr-only">Choose File</span>
<input type="file" name="image"
class="block w-full text-sm text-gray-500 file:mr-4 file:py-2 file:px-4 file:rounded-full file:border-0 file:font-semibold file:bg-blue-50 file:text-blue-700 hover:file:bg-blue-100" />
</label>
@error('image')
<div class="flex items-center text-sm text-red-600">
{{ $message }}
</div>
@enderror
</div>
<button type="submit"
class="text-white bg-blue-600 rounded text-sm px-5 py-2.5">Submit</button>
</form>
</div>
</div>
</div>
</div>
</x-app-layout>
view/blogs/index.blade.php
<x-app-layout>
<x-slot name="header">
<h2 class="text-xl font-semibold leading-tight text-gray-800">
{{ __('Blogs') }}
</h2>
</x-slot>
<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">
<h1 class="text-2xl text-center">Laravel 9 Blog CRUD With image Upload Using Intervention Image
</h1>
<div class="mt-1 mb-4">
<a class="px-2 py-2 text-sm text-white bg-blue-600 rounded"
href="{{ route('blogs.create') }}">{{ __('Add Blog') }}</a>
</div>
<div class="relative overflow-x-auto shadow-md sm:rounded-lg">
<table class="w-full text-sm text-left text-gray-500 dark:text-gray-400">
<thead
class="text-xs text-gray-700 uppercase bg-gray-50 dark:bg-gray-700 dark:text-gray-400">
<tr>
<th scope="col" class="px-6 py-3">
#
</th>
<th scope="col" class="px-6 py-3">
Title
</th>
<th scope="col" class="px-6 py-3">
Image
</th>
<th scope="col" class="px-6 py-3">
Edit
</th>
<th scope="col" class="px-6 py-3">
Show
</th>
<th scope="col" class="px-6 py-3">
Delete
</th>
</tr>
</thead>
<tbody>
@foreach ($blogs as $blog)
<tr class="bg-white border-b dark:bg-gray-800 dark:border-gray-700">
<th scope="row"
class="px-6 py-4 font-medium text-gray-900 dark:text-white whitespace-nowrap">
{{$blog->id}}
</th>
<td class="px-6 py-4">
{{$blog->title}}
</td>
<td class="px-6 py-4">
{{-- <img src="{{ $blog->getFirstMediaUrl('images') }}" alt="no image"
class="w-12 h-12"> --}}
<img src="{{ asset($blog->image) }}" alt="{{ asset($blog->image) }}"
class="w-12 h-12">
</td>
<td class="px-6 py-4">
<a href="{{ route('blogs.edit',$blog->id) }}">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
stroke-width="1.5" stroke="currentColor" class="w-6 h-6 text-blue-600">
<path stroke-linecap="round" stroke-linejoin="round"
d="M16.862 4.487l1.687-1.688a1.875 1.875 0 112.652 2.652L10.582 16.07a4.5 4.5 0 01-1.897 1.13L6 18l.8-2.685a4.5 4.5 0 011.13-1.897l8.932-8.931zm0 0L19.5 7.125M18 14v4.75A2.25 2.25 0 0115.75 21H5.25A2.25 2.25 0 013 18.75V8.25A2.25 2.25 0 015.25 6H10" />
</svg>
</a>
</td>
<td class="px-6 py-4">
<a href="{{ route('blogs.show',$blog->id) }}">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
stroke-width="1.5" stroke="currentColor" class="w-6 h-6 text-blue-600">
<path stroke-linecap="round" stroke-linejoin="round"
d="M2.036 12.322a1.012 1.012 0 010-.639C3.423 7.51 7.36 4.5 12 4.5c4.638 0 8.573 3.007 9.963 7.178.07.207.07.431 0 .639C20.577 16.49 16.64 19.5 12 19.5c-4.638 0-8.573-3.007-9.963-7.178z" />
<path stroke-linecap="round" stroke-linejoin="round"
d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
</svg>
</a>
</td>
<td class="px-6 py-4">
<form action="{{ route('blogs.destroy',$blog->id) }}" method="POST"
onsubmit="return confirm('{{ trans('are You Sure ? ') }}');"
style="display: inline-block;">
<input type="hidden" name="_method" value="DELETE">
<input type="hidden" name="_token" value="{{ csrf_token() }}">
<input type="submit" class="px-4 py-2 text-white bg-red-700 rounded"
value="Delete">
</form>
</td>
</tr>
@endforeach
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</x-app-layout>
view/blogs/edit.blade.php
<x-app-layout>
<x-slot name="header">
<h2 class="text-xl font-semibold leading-tight text-gray-800">
{{ __('Blogs Edit') }}
</h2>
</x-slot>
<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 method="POST" action="{{ route('blogs.update',$blog->id) }}" enctype="multipart/form-data">
@csrf
@method('put')
<div class="mb-6">
<label class="block">
<span class="text-gray-700 @error('title') text-red-500 @enderror">Title</span>
<input type="text" name="title"
class="block @error('title') border-red-500 bg-red-100 text-red-900 @enderror w-full mt-1 rounded-md"
placeholder="" value="{{old('title',$blog->title)}}" />
</label>
@error('title')
<div class="flex items-center text-sm text-red-600">
{{ $message }}
</div>
@enderror
</div>
<div class="mb-6">
<label class="block">
<span class="text-gray-700 @error('content') text-red-500 @enderror">Content</span>
<textarea
class="block @error('content') border-red-500 bg-red-100 text-red-900 @enderror w-full mt-1 rounded-md"
name="content" rows="3">{{old('content',$blog->content)}}</textarea>
</label>
@error('content')
<div class="flex items-center text-sm text-red-600">
{{ $message }}
</div>
@enderror
</div>
<div class="mb-6">
<label class="block">
<span class="sr-only">Choose File</span>
<input type="file" name="image"
class="block w-full text-sm text-gray-500 file:mr-4 file:py-2 file:px-4 file:rounded-full file:border-0 file:font-semibold file:bg-blue-50 file:text-blue-700 hover:file:bg-blue-100" />
</label>
@error('image')
<div class="flex items-center text-sm text-red-600">
{{ $message }}
</div>
@enderror
<img src="{{ asset($blog->image) }}" class="w-12 h-12" alt="{{ asset($blog->image) }}">
</div>
<button type="Update"
class="text-white bg-blue-600 rounded text-sm px-5 py-2.5">Update</button>
</form>
</div>
</div>
</div>
</div>
</x-app-layout>
view/blogs/show.blade.php
<x-app-layout>
<x-slot name="header">
<h2 class="text-xl font-semibold leading-tight text-gray-800">
{{ __('Blog Show') }}
</h2>
</x-slot>
<div class="py-12">
<div class="max-w-4xl 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="flex flex-col items-center justify-center">
<h1 class="text-2xl font-bold">{{ $blog->title }}</h1>
<div>{!! $blog->content !!}</div>
<img src="{{ asset($blog->image) }}" class="w-60 h-60"alt="{{ asset($blog->image) }}">
</div>
</div>
</div>
</div>
</div>
</x-app-layout>
Step 7: Run Laravel and vite server
php artisan serve
# and
npm run dev