How to Upload Images with Spatie Media Library Laravel 9

This is a Laravel 9 Spatie media-library tutorial, where we will learn how to integrate the spatie media library in a Laravel application. Let’s cover a perfect example of how to upload images using the spatie media library in a Laravel application from scratch.

We will first create a simple registration form with name, email and image input fields and upload avatar images with other form fields using the Laravel Spatie media library package.

Laravel is an extensive quintessential PHP framework, where you can build robust web applications and web APIs. It comes with ready-made programming solutions which can rapidly enhance web development speed.

However, it doesn’t provides us with an image upload feature. So, in this guide, we will cover ‘how to build an image or avatar’ with Laravel form using spatie media library.

So, Let’s start implementing Laravel media-library in Laravel to upload the image using Spatie. This library comes with many different features, you can explore more from the official documentation of Laravel Media Library. It is a gold mine of multiple options that profoundly supports Laravel’s eloquent style.

Laravel 9 Upload Avatar Images using Spatie Media Library Example

  • Step 1: Download Laravel App
  • Step 2: Update Database Details
  • Step 3: Install Spatie Medialibrary in Laravel
  • Step 4: Set Up Migration and Model
  • Step 5: Build Controller File
  • Step 6: Build New Routes
  • Step 7: Set Up Blade View Files
  • Step 8: Add App URL
  • Step 9: Run Laravel App

Download Laravel App

Start the first step by using the composer command shown below to create the latest version of a new Laravel application, go to your terminal or command prompt and execute the command below.

composer create-project laravel/laravel my-demo-app --prefer-dist

Update Database Details

Open your .env file and update your database credentials like database name, username and password as shown below.

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=db
DB_USERNAME=root
DB_PASSWORD=

In case you are using MAMP local server in macOS; assure to append UNIX_SOCKET and DB_SOCKET below database credentials in .env file.

UNIX_SOCKET=/Applications/MAMP/tmp/mysql/mysql.sock
DB_SOCKET=/Applications/MAMP/tmp/mysql/mysql.sock

Install Spatie Media Library

It is easy to install the Media library using a composer command. If you want to only use the base package, use the command given below.

composer require "spatie/laravel-medialibrary:^9.6.0"

Let us now prepare the database, you have to publish migration to create a media table for that.

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

Consequently, you have to execute a command below to run migrations.

php artisan migrate

Set Up Migration and Model

Next, you have to generate “Client” migration and model files concurrently using the command given below.

php artisan make:model Client -m

The suggested command generates, app/Models/Client.php, where you can define your table schema.

<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Spatie\MediaLibrary\InteractsWithMedia;
use Spatie\MediaLibrary\HasMedia;

class Client extends Model implements HasMedia
{
    use HasFactory, InteractsWithMedia;
    
    protected $fillable = [
        'name',
        'email',
    ];
}

Make sure to import InteractsWithMedia and HasMedia services as shown above, append HasMedia with implements, and define InteractsWithMedia right after HasFactory service in your model file.

Secondly, open the app/database/migrations/create_clients_table.php file, and add the table values into this migration file.

<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateClientsTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('clients', function (Blueprint $table) {
            $table->id();
            $table->string('name');
            $table->text('email');            
            $table->timestamps();
        });
    }
    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('clients');
    }
}

Build Controller File

Now, go to your terminal and execute command below to make a new controller.

php artisan make:controller ClientController

After running the command above, a new controller file creates at app/Http/Controllers/ClientController.php path.

Import the Client model in this file, then use your ClientController class to store client registration form along with upload image into the database at the same time inside the Medialibrary storage using the spatie media library.

<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Models\Client;

class ClientController extends Controller
{
    public function index()
    {    
        $clients = Client::latest()->get();
        return view('index', compact('clients'));
    }
    public function create()
    {
        return view('create');
    }
    
    public function store(Request $request)
    {
        $input = $request->all();
        $client = Client::create($input);
        if($request->hasFile('avatar') && $request->file('avatar')->isValid()){
            $client->addMediaFromRequest('avatar')->toMediaCollection('avatar');
        }
        return redirect()->route('client');
    }
}

Build New Routes

Next, we will build new routes to handle the controller’s functions. Open your routes/web.php file and define the three routes shown below with Get and Post methods.

<?php
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\ClientController;

/*
|--------------------------------------------------------------------------
| Web Routes
|--------------------------------------------------------------------------
*/

Route::get('client',[ClientController::class,'index'])->name('client');
Route::get('client/create',[ClientController::class,'create'])->name('client.create');
Route::post('client/store',[ClientController::class,'store'])->name('client.store');

Set Up Blade View Files

Now, we are ready to create view files. Create two new view files, where we will create a form for user registration and a file to show clients data after fetching it from the database.

Import bootstrap 5, create the form tag, pass the action tag along with the route, which stores the name, email and avatar profile image into the database.

Next, make create.php and update your code in the app/resources/views/create.blade.php file as shown below.

<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Add Spatie Medialibrary in Laravel</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body class="bg-info">
    <div class="container">
        <div class="d-flex p-2 bd-highlight mb-3">
            <a href="{{ route('client') }}" class="btn btn-outline-danger btn-sm">Go Back</a>
        </div>
        <div>
            <form action="{{ route('client.store') }}" enctype="multipart/form-data" method="post">
                @csrf
                <div class="mb-3">
                    <label>Name</label>
                    <input type="text" name="name" class="form-control">
                </div>
                <div class="mb-3">
                    <label>Email</label>
                    <input type="email" name="email" class="form-control">
                </div>
                <div class="mb-3">
                    <label>Image:</label>
                    <input type="file" name="avatar" class="form-control">
                </div>
                <div class="d-grid">
                    <button class="btn btn-primary">Store</button>
                </div>
            </form>
        </div>
    </div>
</body>
</html>

Create an index.php file and add the code shown below in your app/resources/views/index.blade.php file.

<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Integrate Spatie Medialibrary in Laravel</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
    <div class="container">
        <div class="d-flex p-2 bd-highlight mb-3">
            <a href="{{ route('client.create') }}" class="btn btn-dark">Add</a>
        </div>
        <table class="table">
            <thead>
                <tr>
                    <th>#</th>
                    <th>Name</th>
                    <th>Email</th>
                    <th width="30%">Avatar</th>
                </tr>
            </thead>
            <tbody>
                @foreach($clients as $key=>$item)
                <tr>
                    <td>{{ ++$key }}</td>
                    <td>{{ $item->name }}</td>
                    <td>{{ $item->email }}</td>
                    <td><img src="{{$item->getFirstMediaUrl('avatar', 'thumb')}}" / width="120px"></td>
                </tr>
                @endforeach
            </tbody>
        </table>
    </div>
</body>
</html>

Add App URL

In this step, we will open the .env configuration file, look for the APP_URL variable, and append the given URL to it.

...
...
...
APP_URL = http://localhost:8000
...
...
...

Run Laravel App

By default, the public disk uses the local driver and stores its files in storage/app/public.

To make these files accessible from web, we can create a representative link from public/storage to storage/app/public.

Hence, we can create the symbolic link to access the storage directory using the artisan command publicly.

php artisan storage:link

Finally, start your app using the php artisan command below.

php artisan serve

Use this link which will help you open your application in browser and test it.

http://localhost:8000/client
Laravel Upload Images with Spatie Media Library Example

Conclusion

To conclude, in this tutorial we have discussed few of the best features of Laravel framework, i.e. Laravel File System and Laravel Spatie.

Laravel gives an amazing filesystem abstraction. In addition, it offers a great way to deal with simple drivers to efficiently work with local filesystems, SFTP, and Amazon S3.

If your storage requirement changes while developing. You can quickly switch between these storage options. Between your local development machine and production server as the API remains intact for every system.