binggle's avatar

livewire file upload validation not working .

I wanted to check which validation way is correct.

I tried 3 ways to upload mediafile which more than 30M .

and the validation limit of uploading file is up to 1 MB.

So I used 'max:1024'.

  1. validating when file saves.

this validation works, but not shows error message on view.

class FilesUpload extends Component {
    public $files = [];

    function saveUpload(){
        $this->validate([
            'files.*' => 'file|mimes:mp4,mkv,srt,txt|max:1024', // 1MB Max
        ]);
        // do Save..
    }
}


2 . validating when file updated. this validation DOES NOT work, it passed through validation.

class FilesUpload extends Component {
    public $files = [];

    public function updatedFiles()
    {
        $this->validate([
            'files.*' => 'file|mimes:mp4,mkv,srt,txt|max:1024', // 1MB Max
        ]);
    }

    function saveUpload(){
        // skip validation here.
        // do Save..
    }
}

3 . validating with laravel style(?). Also this validation DOES NOT work, it passed through validation.

class FilesUpload extends Component {
    public $files = [];
    protected $rules = [
        'files.*' => 'file|mimes:mp4,mkv,srt,txt|max:1024', // 1MB Max
    ];

    function saveUpload(){
        // skip validation here.
        // do Save..
    }
}

I used alpine.js for controlling upload. Following is the view.

// files-upload.blade.php


<div x-data="fileUpload()">
    <input type="file" @change="handleFileSelect" />
    <div>
        @error('files') <span class="error">{{ $message }}</span> @enderror
    </div>

    <button wire:click="saveUpload()" class="btn btn-primary "> Save </button>
    <script>
        function fileUpload() {
            return {
                isDropping: false,
                isUploading: false,
                progress: 0,
                handleFileSelect(event) {
                    if (event.target.files.length) {
                        this.uploadFiles(event.target.files)
                    }
                },
                uploadFiles(files) {
                    const $this = this;
                    this.isUploading = true;
                    @this.uploadMultiple('files', files, function(success) {
                        $this.isUploading = false;
                        $this.progress = 0;
                    }, function(error) {
                        console.log('error', error);
                    }, function(event) {
                        $this.progress = event.detail.progress
                    })
                },
            }
        }
    </script>
</div>

How can I make livewire validation works correctly ?

0 likes
14 replies
binggle's avatar

@Sinnbeck I did setup it already. and it uploads to storage.

my problem is validation.

Thanks for reply.

Sinnbeck's avatar

@binggle ok good. Start by checking that it hits the hook

public function updatedFiles()
    {
       dd($this->files);
        $this->validate([
            'files.*' => 'file|mimes:mp4,mkv,srt,txt|max:1024', // 1MB Max
        ]);
    }
binggle's avatar

@Sinnbeck

with this

    public function updatedFiles()
    {
        $this->validate([
            'files.*' => 'file|mimes:mp4,mkv,srt,txt|max:1024', // 1MB Max
        ]);
        dd($this->files);
    }

Nothing happens with same file. I mean. it is uploaded maybe in temp directory .

Sinnbeck's avatar

@binggle then it seems to be working. How are you displaying the validation error?

Sinnbeck's avatar

@binggle Yeah it should get to the temp directory. Thats how livewire works. Can you show your full blade file and livewire class ? Then i can try and recreate it

Or at least enough that I can get it to fail

binggle's avatar
    php artisan make:livewire Utils/FilesUpload 

<?php
// app/Http/Livewire/Utils/FilesUpload.php

namespace App\Http\Livewire\Utils;

use Carbon\Carbon;
use Livewire\Component;
use Livewire\WithFileUploads;
use Livewire\TemporaryUploadedFile;

class FilesUpload extends Component
{
    use WithFileUploads;

    public $files = [];
    public $message = '';
    private $saved_disk = 'uploads';

    // protected $rules = [
    //     'files.*' => 'file|mimes:mp4,mkv,srt,txt|max:1024', // 1MB Max
    // ];

    public function updatedFiles()
    {
        $this->validate([
            'files.*' => 'file|mimes:mp4,mkv,srt,txt|max:1024', // 1MB Max
        ]);
        dd($this->files);
    }

    public function render()
    {

        return view('livewire.utils.files-upload');
    }

    function saveUpload(){
        if( ! $this->files ) {
            $this->message = 'file need';
            return 0;
        }
        
        $this->validate([
            'files.*' => 'file|mimes:mp4,mkv,srt,txt|min:1|max:102400', // 100MB Max
        ]);

        foreach( $this->files as $item ){
            $dir =  Carbon::now()->toDateString( 'Ymd') ;

            $ext = pathinfo( $item->getClientOriginalName() , PATHINFO_EXTENSION); 
            $saved_file_name = implode('.',  [(string) Carbon::now()->timestamp, $ext]);
            $saved_temp = $item->storeAs( $dir ,$saved_file_name , $this->saved_disk );

        }
        $this->message = 'done';
    }
}

// resources/views/livewire/utils/files-upload.blade.php

<div x-data="fileUpload()" class="my-12">

    <div class="container mx-auto">
        <label class="flex flex-col items-center justify-center w-1/2 bg-white border shadow cursor-pointer select-none h-1/2 rounded-2xl hover:bg-slate-50" for="file-upload">
            <h3 class="text-3xl">Click here to select files to upload</h3>
            <em class="italic text-slate-400">(Or drag files to the page)</em>
            <div class="bg-gray-300 h-[6px] w-24 mt-3">
                <div class="bg-blue-500 w-28 block border border-slate-900 h-[6px]" style="transition: width 0.5s" :style="`width: ${progress}%;`" x-show="isUploading"></div>
            </div>
        </label>

        <input type="file" id="file-upload" @change="handleFileSelect" class="hidden" />
        @if(count($files))
        <ul class="mt-5 list-disc">
            @foreach($files as $file)
            <li>
                {{$file->getClientOriginalName()}}
                <button class="text-red-500" @click="removeUpload('{{$file->getFilename()}}')">X</button>
            </li>
            @endforeach
        </ul>
        @endif

        <button wire:click="saveUpload()" class="btn btn-primary "> Save </button>
    </div>

    <script>
        function fileUpload() {
            return {
                isDropping: false
                , isUploading: false
                , progress: 0
                , handleFileSelect(event) {
                    if (event.target.files.length) {
                        this.uploadFiles(event.target.files)
                    }
                }
                , handleFileDrop(event) {
                    if (event.dataTransfer.files.length > 0) {
                        this.uploadFiles(event.dataTransfer.files)
                    }
                }
                , uploadFiles(files) {
                    const $this = this;
                    this.isUploading = true
                    @this.uploadMultiple('files', files
                        , function(success) {
                            $this.isUploading = false
                            $this.progress = 0
                        }
                        , function(error) {
                            console.log('error', error)
                        }
                        , function(event) {
                            $this.progress = event.detail.progress
                        }
                    )
                }
                , removeUpload(filename) {
                    @this.removeUpload('files', filename)
                }
            }
        }

    </script>

</div>

// config/filesystems.php

    'disks' => [
        'uploads' => [
            'driver' => 'local',
            'root' => storage_path('uploads'),
        ],

I removed database job parts cause it is not necessary.

Even with this code with dd($this->files); does not work .

Thank you for concerns.

Sinnbeck's avatar

@binggle happy to help. I built something very similar myself and also wrote a tutorial on it (no validation in that though) so I it should be possible to get working

Sinnbeck's avatar
Sinnbeck
Best Answer
Level 102

Just gave it a shot and indeed it adds the files to the array even though the validation fails. The reason you are missing an error message is due to a missing .* in the directive

@error('files.*') <span class="text-red-500 italic text-sm px-3">{{ $message }}</span> @enderror

I might play a bit more with it to see if I can find a clean way of removing resetting the array if it fails (maybe manual validation)

Edit: Got it working

public function updatedFiles()
    {
        $validatedData = Validator::make(
            ['files' => $this->files],
            ['files.*' => 'file|mimes:mp4,mkv,srt,txt|max:1024'],
        // 1MB Max
        );

        if ($validatedData->fails()) {
            $this->files = [];
        }
        $validatedData->validate();
    }
4 likes
binggle's avatar

@Sinnbeck

GREAT. it is working.

Yes your approach is correct for files array.

And I wish livewire documentation would be more kind and detailed.

Thanks for all your concerns.

Please or to participate in this conversation.