I have a Livewire Component Import.php:
<?php
namespace Modules\Donor\app\Livewire\Modals;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Notification;
use Illuminate\Support\Str;
use Laravel\Pennant\Feature;
use Livewire\Attributes\Validate;
use Livewire\Component;
use Livewire\WithFileUploads;
use Maatwebsite\Excel\Facades\Excel;
use Modules\Batch\App\Models\Batchable;
use Modules\Donor\App\Notifications\ImportNotification;
use Modules\Donor\Imports\DonorsImport;
use Modules\MailCode\App\Features\ImportNotificationFeature;
use Modules\MailCode\App\Models\MailCode;
use Modules\User\App\Models\User;
class Import extends Component
{
use WithFileUploads;
#[Validate(['required', 'file', 'mimes:csv,xlsx', 'max:131072'])] // 128MB
public $file;
public $filename;
#[Validate()]
public $mail_group;
public $mail_code;
public $number_records;
public $upload_date;
public $pro_forma_cost;
public $final_cost;
public $listeners = [
'refresh' => '$refresh',
];
public function render()
{
return view('donor::livewire.modals.import');
}
/**
* Refresh livewire component
*/
public function refresh()
{
$this->reset();
$this->dispatch('refresh');
}
/**
* Extract the file name about to be uploaded and send to parse function
*/
public function updatedFile()
{
// Get the filename from the uploaded file
if ($this->file) {
$this->filename = $this->file->getClientOriginalName();
// Example: Parse the filename and fill other form fields
$this->parseFilename($this->filename);
}
}
/**
* Parse file name in order to prefill other inputs
*/
private function parseFilename(string $filename)
{
// Parsing file into parts for filling inputs
$filenameWithoutExtension = substr(basename($filename), 0, strrpos(basename($filename), '.'));
$parts = explode('_', $filenameWithoutExtension);
// Example Filename: IGN_IGN47_0301_Qty10359_Finders
if (count($parts) >= 4) {
$this->mail_group = substr($parts[1], 0, 3);
$this->mail_code = substr($parts[1], 0, strpos($parts[1], 'ph') !== false ? strpos($parts[1], 'ph') : strlen($parts[1]));
foreach ($parts as $key => $part) {
if (stripos($part, 'qty') !== false) {
$this->number_records = str_ireplace('qty', '', $part);
}
}
}
}
public function import()
{
$this->validate();
// Check for duplicates
$duplicate = MailCode::where('mail_group', $this->mail_group)
->where('mail_code', $this->mail_code)
->where('number_records', $this->number_records)
->where('file_name', $this->filename)
->first();
if ($duplicate) {
session()->flash('error', 'A record with the same mail group, mail code, number of records, and file name already exists.');
return;
}
// Proceed with import if no duplicates are found.
activity('donors.import')->log("Importing file: {$this->filename}");
// Generate one UUID for the entire file
$batch = Batchable::create([
'number' => (string) Str::uuid(),
]);
Excel::import(new DonorsImport($batch->number), $this->file); // Import the file.
// Notifications.
if (Feature::active(ImportNotificationFeature::class)) {
$data = [
'batch' => 1000,
'start' => session('import.start'),
'end' => session('import.end'),
'lines' => [
['type' => 'line', 'value' => 'Your file: '.$this->filename.' has been uploaded sucessfully!'],
['type' => 'line', 'value' => 'The importing of this file will begin soon and we will update you once it has been completed.'],
['type' => 'action', 'text' => 'View Import Progress', 'url' => config('app.url').'/mailcode'],
['type' => 'line', 'value' => 'Thank you for using '.config('app.name').'!'],
],
];
$user = User::find(Auth::id());
Notification::send($user, new ImportNotification($data));
}
$mailCode = MailCode::create([
'mail_group' => $this->mail_group,
'mail_code' => $this->mail_code,
'number_records' => $this->number_records,
'upload_date' => now()->toDateString(),
'pro_forma_cost' => $this->pro_forma_cost,
'final_cost' => $this->final_cost,
'file_name' => $this->filename,
]);
$batch->update([
'batchable_id' => $mailCode->id,
'batchable_type' => get_class($mailCode),
]);
$this->refresh();
session()->flash('status', 'File Uploaded and Mail Code created successfully.');
$this->close();
// Check if the request is coming from /donors and redirect if so
if (str_contains(request()->headers->get('referer'), '/donors')) {
return redirect()->to('/mailcode');
}
}
public function close(): void
{
$this->reset('file');
}
/**
* Define the validation rules
*/
protected function rules(): array
{
return [
'mail_group' => 'required|string|max:10',
'mail_code' => 'required|string|max:10',
'number_records' => 'required|string|max:15',
// 'upload_date' => 'required|date',
'pro_forma_cost' => 'nullable|string|max:50',
'final_cost' => 'nullable|string|max:50',
];
}
}
With Blade View import.blade.php:
<form enctype="multipart/form-data" wire:submit="import">
<div aria-hidden="true" aria-labelledby="staticBackdropLabel" class="modal fade" data-bs-backdrop="static"
id="import-donors" style="display: none;" tabindex="-1" wire:ignore.self x-data="{ uploading: false, progress: 0 }"
x-on:livewire-upload-cancel="uploading = false" x-on:livewire-upload-error="uploading = false"
x-on:livewire-upload-finish="uploading = false"
x-on:livewire-upload-progress="progress = $event.detail.progress" x-on:livewire-upload-start="uploading = true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header bg-primary">
<h5 class="modal-title text-white" id="staticBackdropLabel">{{ __('Import') }}</h5>
<button aria-label="{{ __('Close') }}" class="btn p-1" data-bs-dismiss="modal" type="button">
<span class="fas fa-times fs--1 text-white"></span>
</button>
</div>
<div class="modal-body">
<x-alert />
<div class="row mb-3">
<div class="-mb-3">
<input class="form-control" id="file" type="file" wire:model="file" />
@error('file')
<div class="form-text">{{ $message }}</div>
@enderror
</div>
{{-- <div wire:loading wire:target="file">Uploading...</div> --}}
<!-- Progress Bar -->
<div x-show="uploading">
<progress class="w-100 m-0 pt-1" max="100" x-bind:value="progress"></progress>
</div>
</div>
<div class="row">
<div class="mb-3 col-md-4">
<input class="form-control" id="mail_group" placeholder="{{ __('Mail Group') }}" type="text"
wire:model.blur="mail_group" required />
@error('mail_group')
<div class="form-text">{{ $message }}</div>
@enderror
</div>
<div class="mb-3 col-md-4">
<input class="form-control" id="mail_code" placeholder="{{ __('Mail Code') }}" type="text"
wire:model.blur="mail_code" required />
@error('mail_code')
<div class="form-text">{{ $message }}</div>
@enderror
</div>
{{-- <div class="mb-3 col-md-4">
<input class="form-control" id="upload_date" placeholder="{{ __('Upload Date') }}" type="date"
wire:model.blur="upload_date" required />
@error('upload_date')
<div class="form-text">{{ $message }}</div>
@enderror
</div> --}}
<div class="mb-3 col-md-4">
<input class="form-control" id="pro_forma_cost" placeholder="{{ __('Pro Forma Cost') }}" type="text"
wire:model.blur="pro_forma_cost" />
@error('pro_forma_cost')
<div class="form-text">{{ $message }}</div>
@enderror
</div>
<div class="mb-3 col-md-4">
<input class="form-control" id="final_cost" placeholder="{{ __('Final Cost') }}" type="text"
wire:model.blur="final_cost" />
@error('final_cost')
<div class="form-text">{{ $message }}</div>
@enderror
</div>
<div class="mb-3 col-md-4">
<input class="form-control" id="number_records" placeholder="{{ __('# of Records') }}" type="text"
wire:model.blur="number_records" required />
@error('number_records')
<div class="form-text">{{ $message }}</div>
@enderror
</div>
</div>
</div>
<div class="modal-footer">
<button class="btn btn-primary" type="submit" wire:loading.attr="disabled">
<span wire:loading.remove>
<i aria-hidden="true" class="fa fa-paper-plane"></i>
Submit
</span>
<span wire:loading>
<i aria-hidden="true" class="fa fa-spinner fa-spin"></i>
Submitting...
</span>
</button>
<button class="btn btn-outline-primary" data-bs-dismiss="modal" type="button" wire:click="close"
wire:loading.attr="disabled">
<i aria-hidden="true" class="fa fa-times-circle"></i>
Cancel
</button>
</div>
{{-- @csrf --}}
</div>
</div>
</div>
</form>
The Issue
The issue I am facing is that when I select a file, the /upload-file route is not triggered. Therefore, the updatedFile() function is not triggered.
Additional Info / Current Troubleshooting
- This laravel 11 app is running in Docker.
- When I mount the volume to my local directory, everything works fine, but when I do not mount it, it does not work.
- I have run multiple checks to find the difference in environment between my local system and the Docker container and cannot find anything different that would be relevant to the problem.
- Livewire is correctly applied (I can run
Livewirein the Dev Console and it works. - File and Directory permissions are all correct and it is able to write to the
storage/framework/livewire-tmpdirectory (even though it does not when not working). - No errors in the Error Log and no other indications from anything else.
Any help to point me in the right direction for further troubleshooting would be amazing!