Search code examples
laravelfile-uploadlaravel-bladelaravel-livewire

How to upload pdf file with livewire (Laravel)?


Here is my current code:

Here is my current wire:model called Spk:

<?php

namespace App\Http\Livewire\Spk;

use App\Models\Spk;
use Illuminate\Support\Facades\Storage;
use Livewire\Component;
use Livewire\WithFileUploads;

class Form extends Component
{
    use WithFileUploads;

    public $spk;

    public $submitButtonName;

    protected $rules = [
        'spk.file_path' => 'required|mimes:pdf,xlsx,xls,csv,txt,png,gif,jpg,jpeg|max:8192',
    ];


    public function mount() {
        // Check if the spk property is set
        if (!$this->spk){
            $this->spk = new Spk();

            $this->submitButtonName = 'Create';
        } else {
            $this->submitButtonName = 'Edit';
        }
    }

    public function save()
    {
        $this->validate();

        // Handle file upload
        if ($this->spk->file_path) {
            // Generate a unique filename with microtime
            $filename = 'spk' . microtime(true) . '.' . $this->spk->file_path->getClientOriginalExtension();

            // Save the file to the storage directory
            $this->spk->file_path->storeAs('spk', $filename, 'public');

            // Update the file_path attribute with the new filename
            $this->spk->file_path = $filename;
        }

        $this->spk->save();
        session()->flash('message', 'SPK Saved!');
        return redirect()->route('spks.index');
    }

    public function render()
    {
        return view('livewire.spk.form');
    }
}

Here is my current wire blade view:

<form wire:submit.prevent="save" method="POST" enctype="multipart/form-data">
    @csrf
    <div class="row">
        <div class="px-4 py-2">
            <x-label for="file_path" value="{{ __('File Path') }}" />
            <x-input wire:model="spk.file_path" type="file" name="file_path" :value="old('file_path')" class="w-full"
                required autofocus />
            @error('spk.file_path')
                <div>{{ $message }}</div>
            @enderror
        </div>

        <div class="px-4 pt-8 pb-4">
            <button type="submit"
                class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded-md w-full">
                {{ __($submitButtonName) }}
            </button>
        </div>
    </div>
</form>

I already do php artisan storage:link

I'm confused what's wrong with my code. file_path can't be saved on database. Images when file submited

I try to do dd($this->spk->file_path); and here is the result. It show "disk" => "local" Image when debugging using dd

Please help me to resolve this file upload using livewire. Thanks!


Solution

  • I've made some minor modifications, where it now uploads a file on its own property of the class, rather than to use model-binding to a property of a model. Binding to a models property can be problematic with hydrating/re-hydrating properties - in general, its considered a better practice to not use model-binding (in Livewire v3, its disabled by default).

    Another noteworthy change is replacing the required rule with nulllable - this may or not be correct to change (you would need to determine that yourself), but since you had the explicit check for if ($this->spk->file_path) {, I assumed that it would be possible to submit the form without changing the file.

    <?php
    
    namespace App\Http\Livewire\Spk;
    
    use App\Models\Spk;
    use Illuminate\Support\Facades\Storage;
    use Livewire\Component;
    use Livewire\WithFileUploads;
    
    class Form extends Component
    {
        use WithFileUploads;
    
        public $spk;
        public $spkFile;
    
        public $submitButtonName;
    
        protected $rules = [
            'spkFile' => 'nullable|mimes:pdf,xlsx,xls,csv,txt,png,gif,jpg,jpeg|max:8192',
        ];
    
    
        public function mount() {
            // Check if the spk property is set
            if (!$this->spk){
                $this->spk = new Spk();
    
                $this->submitButtonName = 'Create';
            } else {
                $this->submitButtonName = 'Edit';
            }
        }
    
        public function save()
        {
            $this->validate();
    
            // Handle file upload
            if ($this->spkFile) {
                // Generate a unique filename with microtime
                $filename = 'spk' . microtime(true) . '.' . $this->spkFile->getClientOriginalExtension();
    
                // Save the file to the storage directory
                $this->spkFile->storeAs('spk', $filename, 'public');
    
                // Update the file_path attribute with the new filename
                $this->spk->file_path = $filename;
                $this->spkFile = null;
            }
    
            $this->spk->save();
            session()->flash('message', 'SPK Saved!');
            return redirect()->route('spks.index');
        }
    
        public function render()
        {
            return view('livewire.spk.form');
        }
    }
    
    <form wire:submit.prevent="save" method="POST" enctype="multipart/form-data">
        @csrf
        <div class="row">
            <div class="px-4 py-2">
                <x-label for="file_path" value="{{ __('File Path') }}" />
                <x-input wire:model="spkFile" type="file" name="file_path" :value="old('file_path')" class="w-full"
                    required autofocus />
                @error('spkFile')
                    <div>{{ $message }}</div>
                @enderror
            </div>
    
            <div class="px-4 pt-8 pb-4">
                <button type="submit"
                    class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded-md w-full">
                    {{ __($submitButtonName) }}
                </button>
            </div>
        </div>
    </form>