The Spatie Media Library simplifies the process of handling media files, such as images, in Laravel. It provides features for storage, manipulation, and retrieval of media files. One of the strengths of the package is that you can easily associate uploaded files with your Eloquent Models.
In this guide, you will learn how to integrate the Media Library package into an application. We’ll use it to associate uploaded images with an Eloquent Model, specifically the Product
model.
We’ll cover all the required steps like the package installation process, model setup, defining a migration, creating the controller, views and routes to show products, show a product form with upload and storing new products.
Let’s get started!
Step 1: Install Laravel
Begin by creating a new Laravel project using Composer by running:
composer create-project --prefer-dist laravel/laravel product-media-demo
Step 2: Install Spatie Media Library
Next, install the Spatie Media Library package via Composer. Run the following command in your terminal:
composer require "spatie/laravel-medialibrary:^11.0.0"
After this has finished, we need to run following command to publish a migration the media library requires to work properly:
php artisan vendor:publish --provider="Spatie\MediaLibrary\MediaLibraryServiceProvider" --tag="medialibrary-migrations"
What this publishing does is it creates a file like, for example database/migrations/2024_02_26_203939_create_media_table.php
. This migration serves to define a table ‘media’ with all the columns predefined as they are required by media library.
Afterwards we need to execute this migration by running:
php artisan migrate
The Spatie Media Library has now been successfully installed.
Step 3: Create Model and Migration
Now, let’s create a “Product” model along with its migration by running:
php artisan make:model Product -m
Step 4: Add Migration Code
Open the generated migration file (database/migrations/YYYY_MM_DD_create_products_table.php
) and add additional columns that could be encountered in a real-world product application, such as “name,” “description,” “price,” and “image”:
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up()
{
Schema::create('products', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->text('description')->nullable();
$table->decimal('price', 8, 2);
$table->string('image')->nullable(); // Added for image path
$table->timestamps();
});
}
public function down()
{
Schema::dropIfExists('products');
}
};
Step 5: Run Migration
Now, run the migration to create the products
table in the database:
php artisan migrate
Step 6: Add Model Code
Open the “Product” model (app\Models\Product.php
) and configure it to use the Spatie Media Library trait and implement the HasMedia interface.
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Spatie\MediaLibrary\HasMedia;
use Spatie\MediaLibrary\InteractsWithMedia;
class Product extends Model implements HasMedia
{
use InteractsWithMedia;
protected $fillable = ['name', 'description', 'price', 'image'];
}
Defining our Model this way allows us to associate media, like an uploaded image, with each instance of a Product
. We’ll see exactly how this is used, when we add our controller code in step 9.
Step 7: Create Controller
Generate a controller named “ProductController” by running the following Artisan command:
php artisan make:controller ProductController
Step 8: Add Controller Code
Now open the generated “ProductController” (app\Http\Controllers\ProductController.php
) and implement the methods below to list an index page with products, show a create product form and finally storing a new product along with its uploaded image:
<?php
namespace App\Http\Controllers;
use App\Models\Product;
use Illuminate\Http\Request;
class ProductController extends Controller
{
// Display a list of created products along with their images
public function index()
{
$products = Product::latest()->paginate(10);
return view('products.index', compact('products'));
}
// Display the form to create a new product
public function create()
{
return view('products.create');
}
// Store a new product along with its uploaded image file
public function store(Request $request)
{
$request->validate([
// Sets a max image upload file size of 2048 kilobytes or 2MB, adjust as needed:
'image' => 'required|image|max:2048',
// Validate that other fields contain proper values too:
'name' => 'required|string|max:255',
'description' => 'nullable|string|max:255',
'price' => 'required|numeric|min:0|max:999999.99',
]);
// First create the product in the database using the Eloquent model
$product = Product::create($request->validated());
// Then associate the uploaded image file with the product
$product->addMediaFromRequest('image')->toMediaCollection('product-images');
// Send the user back to the product list page
return redirect(route('products.index'))->with('success', 'Product created successfully.');
}
}
Step 9: Add a View to Create a Product
In this step we’ll create a view that contains the form to add a new product along with an uploaded image.
I made sure the blade code offers a nicely layouted form that shows error messages for missing or invalid values. This way we can guarantee the user is prompted to always upload an actual image file and fill the required fields name
and price
.
Create a file at resources/views/products/create.blade.php
and add the following code:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Excel Import</title>
<!-- Bootstrap CSS -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<div class="container mt-5">
<h1 class="my-4">Create Product</h1>
<form action="{{ route('products.store') }}" method="POST" enctype="multipart/form-data">
@csrf
<div class="mb-3">
<label for="name" class="form-label">Name</label>
@error('name')
<div class="text-danger">
<small>{{ $message }}</small>
</div>
@enderror
<input type="text" class="form-control" id="name" name="name">
</div>
<div class="mb-3">
<label for="description" class="form-label">Description</label>
@error('description')
<div class="text-danger">
<small>{{ $message }}</small>
</div>
@enderror
<textarea class="form-control" id="description" name="description"></textarea>
</div>
<div class="mb-3">
<label for="price" class="form-label">Price</label>
@error('price')
<div class="text-danger">
<small>{{ $message }}</small>
</div>
@enderror
<input type="number" step="0.01" class="form-control" id="price" name="price">
</div>
<div class="mb-3">
<label for="image" class="form-label">Product Image</label>
@error('image')
<div class="text-danger">
<small>{{ $message }}</small>
</div>
@enderror
<input type="file" class="form-control" id="image" name="image">
</div>
<button type="submit" class="btn btn-primary">Create Product</button>
</form>
</div>
</body>
</html>
Step 10: Create a View to Show Products
In this step we’ll create a view that allows us to show all products. We will use Spatie Media Library to render a thumbnail of the uploaded product image.
Create a file at resources/views/products/index.blade.php
and add the following code:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Excel Import</title>
<!-- Bootstrap CSS -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<div class="container mt-5">
<!-- Display success message if it was set-->
@if(session('success'))
<div class="alert alert-success">{{ session('success') }}</div>
@endif
<h1 class="my-4">Product Listing</h1>
<table class="table">
<thead>
<tr>
<th colspan="3"></th>
<th class="text-end">
<a href="{{ route('products.create') }}" class="btn btn-primary">Create Product</a>
</th>
</tr>
<tr>
<th>Name</th>
<th>Description</th>
<th>Price</th>
<th>Image</th>
</tr>
</thead>
<tbody>
@foreach($products as $product)
<tr>
<td>{{ $product->name }}</td>
<td>{{ $product->description }}</td>
<td>{{ $product->price }}</td>
<!-- Fetch a thumbnail image based on the product's associated media -->
<td><img src="{{ $product->getFirstMediaUrl('product-images', 'thumb') }}" alt="Product Image" width="140px"></td>
</tr>
@endforeach
</tbody>
</table>
{{ $products->links() }}
</div>
</body>
</html>
Step 11: Define Routes
Finally, before we can test the application, we need to define the routes to list products, show the create form, and store a product.
Open routes/web.php
and add:
<?php
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\ProductController;
Route::get('/products', [ProductController::class, 'index'])->name('products.index');
Route::get('/products/create', [ProductController::class, 'create'])->name('products.create');
Route::post('/products', [ProductController::class, 'store'])->name('products.store');
Step 12: Test the Application
You can now test the application by running the development server:
php artisan serve
Now you can navigate to http://127.0.0.1:8000/products
in your browser to view the product listing. Proceed to http://127.0.0.1:8000/products/create
to access the create product form.
Select an image to upload and fill in the other product details and submit the form. Verify that the product and its associated image are successfully stored in the database.
After storing the form your database contents should look somewhat like this:
Now if you continue and upload a few more you’ll see it nicely displays all the thumbnail images on the product page:
That’s it! You’ve successfully built your first application that uses Spatie Media Library to manage its uploaded media files.
Conclusion
In this tutorial, we’ve explored how to upload images using the Spatie Media Library package in a Laravel application. By following the steps outlined above, you can easily handle image uploads and storage in your Laravel projects.
Using third party packages like this can greatly speed up your development process. Before you use a package in your production application, check that the package is well maintained, supports your Laravel version, is properly documented and does not have lots of unresolved issues posted on their git page.
Spatie is a well-known company that has been developing and maintaining numerous packages for years and provides them for free as open source. The Media Library is one of their most widely used packages and is likely to meet most quality requirements.
I hope this helped you get started in using this handy third party package. Let me know in the comments what you’re using it for, how it went and what issues you may have had with it.
Happy Coding!
References
- Base Installation (Media Library Documentation)
- Preparing Your Model (Media Library Documentation)
- Laravel Media Library GitHub repository