Back to Blog

Published: Jun 13, 2020

Upload files and photos with Laravel Livewire

Hello everybody! Today we are going to build a file upload application in Laravel 7 with the use of Laravel Livewire.

I always found it difficult to build file upload systems with JavaScript frontend frameworks like Vuejs. Luckily is it super easy to implement with Laravel Livewire.

Since last week Laravel Livewire added support for file uploads out of the box. Today we are going to build a file upload system with Laravel Livewire. We are uploading images to the server and saving the path of the images to the database. In this way we get a list of uploaded images. The result should look something like this:

The result

Let’s create a project

We are going to code a real application to test Laravel Livewire’s file upload possibilities. We start by creating a new Laravel project laravel new <project name>.

Then we add the database credentials in the .env file. My .env file looks like this:

DB_CONNECTION=mysql 
DB_HOST=127.0.0.1 
DB_PORT=3306 
DB_DATABASE=laravel7-livewire-file-upload 
DB_USERNAME=root 
DB_PASSWORD=*******

For the styling we are going to use Tailwindcss. I don’t going to cover how to install Tailwindcss. If you want a guide on installing Tailwindcss follow this tutorial.

Create model and migrations

We are going to create a photo model with an database migration php artisan make:model Photo -m.

In the freshly created model we are adding the following code to the file.

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;


class Photo extends Model
{    
    protected $fillable = [ 'file_name' ];
}

Then we open the database migration file and add the following database fields.

Schema::create('photos', function (Blueprint $table) {
    $table->id();
    $table->string('file_name');
    $table->timestamps();
});

Now we need to run php artisan migrate to create the database tables.

With this out of the way we can install Laravel Livewire and start building the file upload mechanism.

Install Livewire

The installation of Laravel Livewire is super easy.

Add the package by running composer require livewire/livewire.

We need to include the JavaScript on every page we are going to use Livewire. Create a new layouts folder inside the resources/views directory.

In the resources/views/layouts folder we are going to create a new file called app.blade.php With the following code:

<!doctype html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">

        <!-- CSRF Token -->
        <meta name="csrf-token" content="{{ csrf_token() }}">

        <title>{{ config('app.name', 'Laravel') }}</title>

        <!-- Scripts -->
        <script src="{{ asset('js/app.js') }}" defer></script>

        <!-- Fonts -->
        <link rel="dns-prefetch" href="//fonts.gstatic.com">
        <link href="https://fonts.googleapis.com/css?family=Nunito" rel="stylesheet">

        <!-- Styles -->
        <link href="{{ asset('css/app.css') }}" rel="stylesheet">

        @livewireStyles
    </head>
    <body>
        <div id="app" class="flex min-h-screen bg-gray-200">
            @yield('content')
        </div>

        @livewireScripts
    </body>
</html>

Make the components

Now we need to make a livewire component. Run php artisan make:livewire image-upload. This command will create the following files in our project:

app/http/Livewire/ImageUpload.php

resources/views/livewire/image-upload.blade.php

We can render a Livewire component as a component on a page. But since we only have the search component on the page, we are going to use Livewire routing.

Replace the code in the routes/web.php file with the following code:

<?php

use Illuminate\Support\Facades\Route;

Route::livewire('/', 'image-upload');

Livewire component design

Now it is time to add this code to the image-upload.blade.php file.

<div class="w-full mt-12">
    <div class="container max-w-2xl mx-auto">
        @if (session()->has('message'))
            <div class="flex items-center px-4 py-3 mb-6 text-sm font-bold text-white bg-green-500 rounded" role="alert">
                <svg class="w-4 h-4 mr-2 fill-current" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"><path d="M12.432 0c1.34 0 2.01.912 2.01 1.957 0 1.305-1.164 2.512-2.679 2.512-1.269 0-2.009-.75-1.974-1.99C9.789 1.436 10.67 0 12.432 0zM8.309 20c-1.058 0-1.833-.652-1.093-3.524l1.214-5.092c.211-.814.246-1.141 0-1.141-.317 0-1.689.562-2.502 1.117l-.528-.88c2.572-2.186 5.531-3.467 6.801-3.467 1.057 0 1.233 1.273.705 3.23l-1.391 5.352c-.246.945-.141 1.271.106 1.271.317 0 1.357-.392 2.379-1.207l.6.814C12.098 19.02 9.365 20 8.309 20z"/></svg>
                <p>{{ session('message') }}</p>
            </div>
        @endif

        <div class="p-6 mb-12 bg-white border rounded-md shadow-xl">
            <form wire:submit.prevent="save">
                <div class="mb-3">
                    <input type="file" wire:model="photo" class="">
                    <div>
                        @error('photo') <span class="text-sm italic text-red-500">{{ $message }}</span>@enderror
                    </div>
                    <div wire:loading wire:target="photo" class="text-sm italic text-gray-500">Uploading...</div>
                </div>

                <button type="submit" class="px-4 py-2 font-bold text-white bg-blue-500 rounded hover:bg-blue-700">Save Photo</button>
            </form>
        </div>

        <div class="flex flex-wrap -mx-2">
            @foreach($photos as $photo)
                <div class="w-1/2 p-2">
                    <div class="w-full h-full border">
                        <img src="{{ asset('storage/photos/' . $photo->file_name) }}">
                    </div>
                </div>
            @endforeach
        </div>
    </div>
</div>

We create a simple file upload form. We use wire:submit.prevent to handle the form submit with Laravel Livewire. We bind Livewire to the file input with wire:model="photo".

Below the file upload form we generate a list of stored photos. We do this by looping trough the $photos array.

Also, note that we have a little alert at the top. We will show a message when the image is successfully uploaded.

Livewire component logic

Next, we will add the logic in the ImageUpload.php file.

<?php

namespace App\Http\Livewire;

use Livewire\Component;
use Livewire\WithFileUploads;
use App\Photo;

class ImageUpload extends Component
{
    use WithFileUploads;

    public $photo;

    public function save()
    {
        $this->validate([
            'photo' => 'image|max:1024',
        ]);

        $name = md5($this->photo . microtime()).'.'.$this->photo->extension();

        $this->photo->storeAs('photos', $name);

        Photo::create(['file_name' => $name]);

        session()->flash('message', 'The photo is successfully uploaded!');
    }

    public function render()
    {
        return view('livewire.image-upload', [
            'photos' => Photo::all(),
        ]);
    }
}

First, we validate the submitted file. We check if it is an image and that the image is not too big.

Then we create a unique name for the image. We do this by hashing the current micro time and adding the file extension.

Then we upload the file to the server and place the name of the photo in the database.

Then we flash a message that the photo is successfully uploaded to the server.

Linking the storage

If you try to upload a file you will see that it is not working yet. To make this all working we must add a symbolic link to the path where we store the photos.

Open config/filesystems.php and add the following code in the links array.

'links' => [
    public_path('storage') => storage_path('app/public'),
    public_path('storage/photos') => storage_path('app/photos'),
],

Then we run the php artisan storage:link command. This will create the symbolic link.

Now the file uploading system should work. You can upload photos and the photos will display in the list beneath the file upload form.

Wrapping it up

Today we have learned how to use Livewire file uploads. You can find the source code on GitHub. If you want to learn more about Livewire, checkout this tutorial about searching in Livewire.

If you have any questions, please let me know in the comments. You can also reach out to me on twitter.

Follow me on twitter to get notified when I post a new blog @larapeakdev