How to Upload Image Full CRUD with Spatie MediaLibrary Package Laravel 9

Hello, my friend! This tutorial will show you how to upload an image using the Spatie MediaLibrary package. You will also learn how to upload images, edit images, and delete images. Today, you will see everything in one place. I’m sure many of you have found yourself in a situation where you don’t edit or remove the image from the post.

Also Read : How to Create Tabs Using Tailwind CSS and Alpine JS

How to Upload Image Full CRUD with Spatie MediaLibrary Package Laravel 9

  • Step 1: Set Up Laravel Project
  • Step 2: Set Up Database Details in ENV
  • Step 3: Create Model and Migration
  • Step 4: Install laravel-medialibrary
  • Step 5: Set Up laravel-medialibrary
  • Step 6: Perform Crud Operations

Set Up Laravel Project

Installing a new Laravel app, so go to the terminal, type the command, and create a new Laravel app.

composer create-project --prefer-dist laravel/laravel laravel_image_crud

Now, You have to move to the project folder:

cd laravel_image_crud

Set Up Database Details in ENV

You must now connect the Laravel app to the database, so open the .env configuration file and enter the database credentials as shown 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

Create Model and Migration

To generate model and migration files, type the recommended command into the terminal screen and press Enter.

php artisan make:model Image -m

Also Read : How to Install Bootstrap in Laravel 9?

You must include the $fillable array and the table values in the app/Models/Image.php file.

<?php
namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Spatie\MediaLibrary\HasMedia\HasMediaTrait;
use Spatie\MediaLibrary\HasMedia;
use Spatie\MediaLibrary\InteractsWithMedia;

class Image extends Model implements HasMedia
{
    use HasFactory, InteractsWithMedia;

    protected $fillable = [
        'name'
    ];
}

Now, add name in migration file

<?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->string('name');
            $table->timestamps();
        });
    }

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

Install laravel-medialibrary

We are using v10 in this project, but you can use an older version if you prefer.

Website : https://spatie.be/docs/laravel-medialibrary/v10/introduction

composer show spatie/laravel-medialibrary

Set Up laravel-medialibrary

Now, You need to publish the migration to create the media table:

php artisan vendor:publish --provider="Spatie\MediaLibrary\MediaLibraryServiceProvider" --tag="migrations"

Now, we to migrate

php artisan migrate

Output:

Migration table created successfully.
Migrating: 2022_05_31_105041_create_images_table
Migrated: 2022_05_31_105041_create_images_table (21.92ms)
Migrating: 2022_05_31_112243_create_media_table
Migrated: 2022_05_31_112243_create_media_table (45.90ms)

Also Read : How to Create & Use Component in Angular 13 App

you can see we got new table media database/migrations/create_media_table.php file.

<?php

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

class CreateMediaTable extends Migration
{
    public function up()
    {
        Schema::create('media', function (Blueprint $table) {
            $table->bigIncrements('id');

            $table->morphs('model');
            $table->uuid('uuid')->nullable()->unique();
            $table->string('collection_name');
            $table->string('name');
            $table->string('file_name');
            $table->string('mime_type')->nullable();
            $table->string('disk');
            $table->string('conversions_disk')->nullable();
            $table->unsignedBigInteger('size');
            $table->json('manipulations');
            $table->json('custom_properties');
            $table->json('generated_conversions');
            $table->json('responsive_images');
            $table->unsignedInteger('order_column')->nullable();

            $table->nullableTimestamps();
        });
    }
}

Set Up laravel-medialibrary

You must now add a few classes to laravel-medialibrary (HasMediaTrait , HasMedia, InteractsWithMedia)

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Spatie\MediaLibrary\HasMedia\HasMediaTrait;
use Spatie\MediaLibrary\HasMedia;
use Spatie\MediaLibrary\InteractsWithMedia;

class Image extends Model implements HasMedia
{
    use HasFactory, InteractsWithMedia;

    protected $fillable = [
        'name'
    ];
}

Now , you need to link storage, so type the command in the terminal and run the command.

php artisan storage:link

Notice: we need to set url path like 8000, or your current server

.env

APP_URL=http://localhost
to
APP_URL=http://localhost:8000

You must have your Domain in production or your image will not be seen.

Perform Crud Operations

Next, type the command in the terminal and run it to generate the Controller and routes.

php artisan make:controller ImageController -r

Create routes

We use the resource route to save time and write much shorter code.

<?php

use App\Http\Controllers\ImageController;
use Illuminate\Support\Facades\Route;

/*
|--------------------------------------------------------------------------
| 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 view('welcome');
});

Route::resource('images', ImageController::class);

Let Implements Code:

<?php

namespace App\Http\Controllers;

use App\Models\Image;
use Illuminate\Http\Request;

class ImageController extends Controller
{
    /**
     * Display a listing of the resource.
     *
     * @return \Illuminate\Http\Response
     */
    public function index()
    {
        $images = Image::all();

        return view('images.index', compact('images'));
    }

    /**
     * Show the form for creating a new resource.
     *
     * @return \Illuminate\Http\Response
     */
    public function create()
    {
        return view('images.create');
    }

    /**
     * Store a newly created resource in storage.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return \Illuminate\Http\Response
     */
    public function store(Request $request)
    {
        $this->validate($request, [
            'name' => 'required'
        ]);

        $image = Image::create([
            'name' => $request->name
        ]);

        if ($image) {
            if ($request->hasFile('image')) {
                $image->addMediaFromRequest('image')->toMediaCollection('images');
            }
        }

        session()->flash('success', 'Image create successfully');

        return redirect()->route('images.index');
    }

    /**
     * Display the specified resource.
     *
     * @param  int  $id
     * @return \Illuminate\Http\Response
     */
    public function show($id)
    {
        //
    }

    /**
     * Show the form for editing the specified resource.
     *
     * @param  int  $id
     * @return \Illuminate\Http\Response
     */
    public function edit($id)
    {
        $image = Image::find($id);

        return view('images.edit', compact('image'));
    }

    /**
     * Update the specified resource in storage.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  int  $id
     * @return \Illuminate\Http\Response
     */
    public function update(Request $request, $id)
    {
        $this->validate($request, [
            'name' => 'required'
        ]);

        $image = Image::find($id);
        $image->name = $request->name;
        $image->save();

        if ($image) {
            if ($request->hasFile('image')) {
                $image->clearMediaCollection('images');
                $image->addMediaFromRequest('image')->toMediaCollection('images');
            }
        }

        session()->flash('success', 'Image Update successfully');

        return redirect()->route('images.index');
    }

    /**
     * Remove the specified resource from storage.
     *
     * @param  int  $id
     * @return \Illuminate\Http\Response
     */
    public function destroy($id)
    {
        $image = Image::find($id);
        $image->delete();

        session()->flash('success', 'Image Delete successfully');

        return redirect()->route('images.index');
    }
}

On Image Store, you can see that we are saving the image to MediaCollection.

if ($image) {
    if ($request->hasFile('image')) {
        $image->addMediaFromRequest('image')->toMediaCollection('images');
    }
}

On Update Image you can see we are clear old image using clearMediaCollection first then store new image

if ($image) {
    if ($request->hasFile('image')) {
        $image->clearMediaCollection('images');
        $image->addMediaFromRequest('image')->toMediaCollection('images');
      }
} 

Also Read : How to Install Alpine.js in Laravel 9

Now,let see blade file view/images/index.blade.php

<!DOCTYPE html>
<html lang="en">

    <head>
        <title>How to Upload Image Full CRUD with Spatie MediaLibrary Package Laravel 9</title>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css">
        <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
        <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.16.0/umd/popper.min.js"></script>
        <script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js"></script>
    </head>

    <body>

        <div class="container mt-5">
            <div class="row">
                @if ($message = Session::get('success'))
                <div class="alert alert-success alert-dismissible" role="alert">
                    <button type="button" class="close" data-dismiss="alert" aria-label="Close">
                    <span aria-hidden="true">&times;</span>
                    </button>
                    {{ $message }}
                </div>
                @endif
                <div class="col-md-12">
                    <h2>How to Upload Image Full CRUD with Spatie MediaLibrary Package Laravel 9</h2>
                    <div class="card">
                        <div class="card-header py-3 d-flex flex-row align-items-center justify-content-between">
                            <a href="{{ route('images.create') }}" class="btn btn-success">Create</a>
                            <h6 class="m-0 font-weight-bold text-primary">Image List</h6>
                        </div>
                    </div>
                    <table class="table">
                        <thead>
                            <tr>
                                <th>#</th>
                                <th>Name</th>
                                <th>Image</th>
                                <th>Edit</th>
                                <th>Delete</th>
                            </tr>
                        </thead>
                        <tbody>
                            @foreach ($images as $image)
                            <tr>
                                <td>{{ $image->id }}</td>
                                <td>{{ $image->name }}</td>
                                <td><img src="{{ $image->getFirstMediaUrl('images') }}" alt="no image" width="100" height="100"></td>
                                <td>
                                    <a class="btn btn-xs btn-primary" href="{{ route('images.edit',$image->id) }}">
                                        Edit
                                    </a>
                                </td>
                                <td>
                                    <form action="{{ route('images.destroy',$image->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="btn btn-xs btn-danger" value="Delete">
                                    </form>
                                </td>
                            </tr>
                            @endforeach
                        </tbody>
                    </table>
                </div>
            </div>

            
        </div>

    </body>

</html>

Now, let see blade file view/images/create.blade.php

<!DOCTYPE html>
<html lang="en">

    <head>
        <title>How to Upload Image Full CRUD with Spatie MediaLibrary Package Laravel 9</title>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css">
        <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
        <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.16.0/umd/popper.min.js"></script>
        <script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js"></script>
    </head>

    <body>

        <div class="container mt-5">
            <div class="row">
                <div class="col-md-12">
                    <h2>Create</h2>
                    <div class="card">
                        <div class="card-header py-3 d-flex flex-row align-items-center justify-content-between">
                            <a href="{{ route('images.create') }}" class="btn btn-success">Create</a>
                            <h6 class="m-0 font-weight-bold text-primary">Create Image</h6>
                        </div>
                    </div>
                </div>
            </div>

            <div class="card-body">
                <form action="{{ route("images.store") }}" method="POST" enctype="multipart/form-data">
                    @csrf

                    <div class="form-group">
                        <label for="Name">Name</label>
                        <input type="text" name="name" class="form-control" placeholder="name">
                        @if($errors->has('name'))
                        <strong class="text-danger">{{ $errors->first('name') }}</strong>
                        @endif
                    </div>

                    <div class="form-group">
                        <div class="mb-3">
                            <label>image file</label>
                            <input type="file" name="image" class="form-control">
                            @if($errors->has('image'))
                            <strong class="text-danger">{{ $errors->first('image') }}</strong>
                            @endif
                        </div>
                    </div>

                    <button type="submit" class="btn btn-primary">Submit</button>
                </form>
            </div>

        </div>

    </body>

</html>

Now,let see blade file view/images/edit.blade.php

<!DOCTYPE html>
<html lang="en">

    <head>
        <title>How to Upload Image Full CRUD with Spatie MediaLibrary Package Laravel 9</title>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css">
        <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
        <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.16.0/umd/popper.min.js"></script>
        <script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js"></script>
    </head>

    <body>

        <div class="container mt-5">
            <div class="row">
                <div class="col-md-12">
                    <h2>Edit</h2>
                    <div class="card">
                        <div class="card-header py-3 d-flex flex-row align-items-center justify-content-between">
                            <a href="{{ route('images.create') }}" class="btn btn-success">Create</a>
                            <h6 class="m-0 font-weight-bold text-primary">Edit Image</h6>
                        </div>
                    </div>
                </div>
            </div>

            <div class="card-body">
                <form action="{{ route("images.update",$image->id) }}" method="POST" enctype="multipart/form-data">
                    @csrf
                    @method('put')
                    <div class="form-group">
                        <label for="Name">Name</label>
                        <input type="text" name="name" class="form-control" placeholder="name"
                        value="{{ old('name',$image->name )}}">
                        @if($errors->has('name'))
                        <strong class="text-danger">{{ $errors->first('name') }}</strong>
                        @endif
                    </div>

                    <div class="form-group">
                        <div class="mb-3">
                            <label>image file</label>
                            <input type="file" name="image" class="form-control">
                            @if($errors->has('image'))
                            <strong class="text-danger">{{ $errors->first('image') }}</strong>
                            @endif
                            <img src="{{ $image->getFirstMediaUrl('images') }}" alt="no image" width="100" height="100">
                        </div>
                    </div>

                    <button type="submit" class="btn btn-primary">Submit</button>
                </form>
            </div>

        </div>

    </body>

</html>

Completed! You can now run the application.

Also Read : React & Tailwind CSS – Reusable Button Component

1 Comment

Leave a Reply