Livewire file upload in Laravel refers to the process of allowing users to upload files using the Livewire framework. Allowing users to upload files is a common requirement. Whether it’s for sharing images, documents, or media files, handling file uploads effectively is very important. It is crucial to follow best practices when implementing file uploads to ensure a smooth and secure user experience. Livewire is a powerful framework that allows developers to build interactive web interfaces using Laravel. It provides a seamless way to handle file upload by leveraging its real-time communication capabilities. In this blog post, we’ll explore some practical tips and best practices for managing file upload using Livewire in Laravel.
Before moving to the example, let’s quickly recap what we are going to achieve today.
In the above form, we have a file input along with two text input fields.
Also, we have the real-time validation using the Livewire. We will be working on the interesting feature of livewire which is file preview.
Yes, Livewire provides a preview option for the uploaded image. So, in the below result, when you select the image, it will be previewed before upload. After that you can submit the form.
After filling above details, when I submit the form, it will be submitted. Also, the image will be uploaded and the details will be stored in the database.
Let’s come to the example now.
Step 1 – Preparing Your Environment For Livewire File Upload
To start using Livewire for file upload in Laravel, you need to install and set up both Livewire and Laravel.
Hence, let’s start first with the Laravel project setup. So, open the terminal and hit the below command to install Laravel.
composer create-project --prefer-dist laravel/laravel livewire-file-upload
Once installed, you can configure the database for the project. So, open the project in any of your editors. Thereafter, come inside the .env file and add the DB configuration.
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE={{DATABASE_NAME}}
DB_USERNAME={{DATABASE_USERNAME}}
DB_PASSWORD={{DATABASE_PASSWORD}}
After the DB configuration, we will move out the Livewire installation in the current Laravel project.
composer require livewire/livewire
Now, you are done with the Livewire and Laravel setup. Hence, let’s move to the next step.
Recommended: Livewire Pagination in Laravel: Making Your App Dynamic and Responsive
Step 2 – Model and Migration Setup For File Upload in Livewire
You need a model and migration for creating the user profile section as per the result. You can create it for the User. However, Laravel has the default User model and migration. Hence, I will be using the same one.
But, you need to update the migration as shown below.
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('users', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('email')->unique();
$table->string('profile_image');
$table->string('profile_image_original');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('users');
}
};
For this migration, you will have to modify the fillable data in the User model.
<?php
namespace App\Models;
// use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Laravel\Sanctum\HasApiTokens;
class User extends Authenticatable
{
use HasApiTokens, HasFactory, Notifiable;
/**
* The attributes that are mass assignable.
*
* @var array<int, string>
*/
protected $fillable = [
'name',
'email',
'profile_image',
'profile_image_original'
];
}
After that, you will have to migrate the schema using the migrate command.
php artisan migrate
Step 3 – Create Livewire Component For File Upload
Creating a file upload component is a fundamental step in Livewire. By implementing file upload functionality, users can select and upload files. It is crucial to validate and store the uploaded files securely to maintain data integrity.
Hence, firstly, you will have to create a Livewire component using the below command.
php artisan make:livewire UserProfile
This command will generate a component having a class and a view file.
In the view file, you will have to design a basic file upload form. So, let’s do that quickly.
Recommended: Building a Dynamic Livewire CRUD Application in Laravel 10
Step 4 – Design User Interface For File Upload in Livewire
The component view file will be located in the resources/views/livewire directory. Hence, open it and add the below snippet.
<div class="container my-2">
<div class="row">
<div class="col-xl-6 m-auto">
{{-- Display flash message --}}
@if (session()->has('success'))
<div class="alert alert-success alert-dismissible fade show">
<svg class="bi flex-shrink-0 me-2" width="24" height="24" role="img" aria-label="Success:">
<use xlink:href="#check-circle-fill" />
</svg>
<strong>Success!</strong> {{ session('success') }}
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
@elseif (session()->has('error'))
<div class="alert alert-danger alert-dismissible fade show">
<svg class="bi flex-shrink-0 me-2" width="24" height="24" role="img"
aria-label="Danger:">
<use xlink:href="#exclamation-triangle-fill" />
</svg>
<strong>Alert!</strong> {{ session('error') }}
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
@endif
{{-- form starts --}}
<form wire:submit.prevent="submitForm">
<div class="card shadow">
<div class="card-header">
<h5 class="card-title fw-bold">User Profile </h5>
</div>
<div class="card-body px-5">
{{-- profile image --}}
<div class="form-group text-center ">
<div class="position-relative">
<label for="profileImage" class="profile-image-label">
{{-- if profile image selected then preview --}}
@if ($profileImage)
<img src="{{ $profileImage->temporaryUrl() }}" width="60" height="60"
class="img-fluid profile-img" />
@else
{{-- show default icon --}}
<span class="img-icon">
<svg xmlns="http://www.w3.org/2000/svg" width="60" height="60"
viewBox="0 0 24 24">
<circle cx="12" cy="12" r="3.2" />
<path
d="M9 2l-1.83 2h-3.17c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2v-12c0-1.1-.9-2-2-2h-3.17l-1.83-2h-6zm3 15c-2.76 0-5-2.24-5-5s2.24-5 5-5 5 2.24 5 5-2.24 5-5 5z" />
<path d="M0 0h24v24h-24z" fill="none" />
</svg>
</span>
@endif
<input type="file" id="profileImage" wire:model="profileImage" class="file-input"
name="profileImage" />
</label>
</div>
{{-- Display message validation error message --}}
<div>
@error('profileImage')
<span id="message-error" class="text-danger">{{ $message }}</span>
@enderror
</div>
</div>
{{-- name --}}
<div class="form-group">
<label for="name">Name <span class="text-danger">*</span></label>
<input type="text" id="name" wire:model="name" placeholder="Name"
aria-describedby="name-error" aria-required="true"
@error('name') aria-invalid="true" @enderror
class="form-control @error('name') is-invalid @enderror">
{{-- Display name validation error message --}}
@error('name')
<span id="name-error" class="text-danger">{{ $message }}</span>
@enderror
</div>
{{-- email --}}
<div class="form-group my-3">
<label for="email">Email <span class="text-danger">*</span></label>
<input type="text" id="email" wire:model="email" placeholder="Email"
aria-describedby="email-error" @error('email') aria-invalid="true" @enderror
class="form-control @error('email') is-invalid @enderror">
{{-- Display email validation error message --}}
@error('email')
<span id="email-error" class="text-danger">{{ $message }}</span>
@enderror
</div>
<div class="form-group mb-2">
<button type="submit" class="btn btn-primary">Submit</button>
</div>
</div>
</div>
</form>
{{-- form ends --}}
</div>
</div>
</div>
Livewire file modeling is similar to the other input binding model. It doesn’t require form attribute enctype=”multipart/form-data” for reading file from the form as Laravel requires.
So, let’ see how you can read and handle file uploading in Livewire.
Step 5 – Implement File Upload Functionality in Livewire
Livewire provides a Trait for file upload handling. It needs to import at the top and then this can be used in the component to read the file from the Form.
use WithFileUploads;
Let’s add this Trait to handle the file upload in Livewire component.
<?php
namespace App\Http\Livewire;
use App\Models\User;
use Livewire\Component;
use Livewire\WithFileUploads;
class UserProfile extends Component
{
// use livewire trait
use WithFileUploads;
// Define public attributes
public $name, $email, $profileImage;
// Component Rendering
public function render()
{
return view('livewire.user-profile');
}
// Define validation rules
protected $rules = [
'name' => 'required',
'email' => 'required|email',
'profileImage' => 'required|image|max:2048'
];
// Define custom validation error message
protected $messages = [
'name.required' => 'Name is required',
'email.required' => 'Email address is required',
'email.email' => 'Email address is not valid',
'profileImage.required' => 'Profile image is required',
'profileImage.image' => 'Profile image is not valid',
'profileImage.image.max' => 'Profile image size is too large',
];
// Validate inputs in realtime
public function updated($inputNames) {
$this->validateOnly($inputNames);
}
/**
* Function : Submit Form
* @param NA
* @return response
*/
public function submitForm() {
// Validate form
$validatedInputs = $this->validate();
// Store profile image in user-profile directory
$this->profileImage->store('user-profile');
$validatedInputs['profile_image'] = $this->profileImage->getFilename();
$validatedInputs['profile_image_original'] = $this->profileImage->getClientOriginalName();
$user = User::create($validatedInputs);
// Reset form inputs
$this->resetInputs();
if ($user) {
session()->flash('success', 'User Profile details saved successfully!');
}
else {
session()->flash('error', 'Unable to save User Profile. Please try again!');
}
}
// Reset form inputs
private function resetInputs() {
$this->name = null;
$this->email = null;
$this->profileImage = null;
}
}
Let me explain what I have done In the above snippet.
- I have used the Livewire trait at the beginning.
- After that, I created public variables to bind form inputs.
- Next, I created the validation rules and the custom validation error messages.
- Then we have the submit form function to handle the form submit request. Initially, the form submit request will validate the inputs including the uploaded file. Then we store the profile image in the storage directory.
- Lastly, we are saving these records into the users table and returning the flash message.
Now, this component needs to be called in a blade file to be executed. So, let’s create a Laravel blade file.
Recommended: Livewire Form Validation: Building Better User Experiences in Laravel
Step 6 – Render Livewire Component
In this step, you will require a controller and a blade file. So, let’s have a controller first.
php artisan make:controller UserController
After having the controller, let’s add the below snippet to render a blade file.
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class UserController extends Controller
{
public function userProfile() {
return view('user');
}
}
After that, you need to create a blade file. So, create a blade file by giving the name user.blade.php.
<!doctype html>
<html lang="en">
<head>
<title>Livewire File Upload in Laravel 10</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
{{-- Bootstrap CSS v5.2.1 --}}
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.1/dist/css/bootstrap.min.css" rel="stylesheet"
integrity="sha384-iYQeCzEYFbKjA/T2uDLTpkwGzCiq6soy8tYaI1GyVh/UjpbCx/TYkiZhlZB6+fzT" crossorigin="anonymous">
{{-- Livewire Styles --}}
@livewireStyles
{{-- Custom CSS --}}
<style>
.file-input::-webkit-file-upload-button {
visibility: hidden;
}
input[type='file'] {
font-size: 10px;
margin-left: 60px
}
::file-selector-button {
font-size: initial;
}
.file-input::before {
content: '';
display: inline-block;
height: 150px;
width: 150px;
border: 1px solid #999;
border-radius: 50%;
padding: 5px 8px;
outline: none;
white-space: nowrap;
-webkit-user-select: none;
cursor: pointer;
text-shadow: 1px 1px #fff;
font-weight: 700;
font-size: 10pt;
}
.file-input:hover::before {
border-color: black;
}
.file-input:active::before {
background: -webkit-linear-gradient(top, #e3e3e3, #f9f9f9);
}
.img-icon {
margin-left: 104px;
cursor: pointer;
position: absolute;
top: 45px;
bottom: 0;
}
.profile-img {
height: 150px;
width: 150px;
position: absolute;
cursor: pointer;
border-radius: 50%;
margin-left: 60px
}
</style>
</head>
<body>
<main class="pt-3 px-5">
<div class="container-fluid">
<h3 class="text-center fw-bold border-bottom pb-2">Livewire File Upload with Preview in Laravel </h3>
<div class="row justify-content-center mt-3">
{{-- Calling Livewire Component --}}
@livewire('user-profile')
</div>
</div>
</main>
{{-- jQuery CDN --}}
<script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
{{-- Bootstrap JavaScript Libraries --}}
<script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.11.6/dist/umd/popper.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.1/dist/js/bootstrap.min.js"></script>
{{-- Livewire Scripts --}}
@livewireScripts
</body>
</html>
That’s done for the Livewire file upload. However, you need a route for rendering this demo application. Therefore, let’s add this.
Step 7 – Route Binding for Livewire File Upload
In order to add the route, you will have to navigate to the web.php file and add the below snippet.
<?php
use App\Http\Controllers\UserController;
use Illuminate\Support\Facades\Route;
Route::get('user-profile', [UserController::class, 'userProfile'])->name('user-profile');
That’s it now, you can run the application to see the result.
Conclusion
In this example, we explored how to implement Livewire file upload in Laravel. We created a Livewire component for handling file upload built a Blade view for the component, and added it to a route. Livewire simplifies the process of creating interactive components, making it easier to handle file upload and other user interactions in your Laravel applications. Feel free to adapt this example to suit your specific requirements, such as custom validation rules or additional file handling logic. You can Improve user experience during file uploads involves implementing progress bars and notifications. This provides users with visual feedback on the upload progress. Allowing users to cancel or retry file uploads gives them more control over the process. Providing visual feedback for completed uploads ensures users are aware of the successful upload. We will see these all advanced file upload guides in upcoming post.
Leave a Reply