I'm trying to upload the avatar via the API endpoint but every time I select a file Livewire resets the field. File upload works well when I tested with Postman. But the issue is using Livewire to consume the endpoint. Live wire just resets the file upload field when an image is selected. The other fields update easily without any issues. It's just the avatar field.
Livewire Form UpdateAccount() Component
<form wire:submit.prevent="submit" enctype="multipart/form-data">
<div class="widget-edit mb-30 avatar dashed-grey-border">
<div class="title">
<h4>Edit your avatar</h4>
<i class="icon-keyboard_arrow_up"></i>
</div>
<div class="uploadfile flex">
<img src="{{ $avatar ? $avatar->temporaryUrl() ?? asset($avatar) : asset('images/avatar/avatar-08.png') }}"
style="width: 125px; height: 125px; object-fit: cover; border-radius: 10%;" alt="">
<div>
<h6>Upload a new Avatar</h6>
<label class="dashed-grey-border">
<input type="file" name="avatar" wire:model="avatar" accept=".jpeg,.png,.svg,.webp">
<span class="text filename">No files selected</span>
</label>
@if(session()->has('avatar_error'))
<p class="tf-gradient-text mt-3 mb-2 fw-light">
{{ session('avatar_error') }}
</p>
@endif
<p class="text">JPEG, PNG, SVG or WEBP - Max 2MB</p>
</div>
</div>
</div>
// Other form fields below : Username, Phone Number, DOB, Address etc.
...
</form>
Livewire UpdateAccount() Component
<?php
namespace App\Livewire\Dash;
use Illuminate\Support\Facades\Http;
use Livewire\Attributes\Layout;
use Livewire\Attributes\Title;
use Livewire\Component;
use Livewire\WithFileUploads;
class UpdateAccount extends Component
{
// Use the WithFileUploads trait to handle file uploads
use WithFileUploads;
/**
* Define layout and title for the component
*/
#[Layout('layouts.dash')]
#[Title('Account Settings')]
// Properties to hold user input and API response
public $access_token;
public $avatar;
public $email_address;
public $username;
public $nationality;
public $phone_number;
public $date_of_birth;
public $gender;
public $address;
public $bio;
public function mount()
{
// Retrieve the access token from the session
if (!session()->has('access_token')) {
// Flash an error message to the session
session()->flash('message', 'Error: Unauthorized action!');
// Redirect to the login page if the session data is missing
return redirect()->route('auth.login');
}
// Assign the access token to the property from the session data
$this->access_token = session('access_token');
// Call the API to get the user's account information
$response = Http::withToken($this->access_token)->get(route('api.account.details'));
// Handle the response based on success or error
$response->successful()
? $this->handleSuccess($response, 'account_details')
: $this->handleFailure($response);
}
/**
* Prepare the modification payload for the API request
*
* @return array
*/
protected function accountDetailsPayload(): array
{
return [
'avatar' => $this->avatar,
'email_address' => $this->email_address,
'username' => $this->username,
'nationality' => $this->nationality,
'phone_number' => $this->phone_number,
'date_of_birth' => $this->date_of_birth,
'gender' => $this->gender,
'address' => $this->address,
'bio' => $this->bio,
'_method' => 'PUT',
];
}
/**
* Handle the form submission for user login
*
* Sends the user input to the login API endpoint and handles the response.
*/
public function submit()
{
// Send the login data to the API
$response = Http::withToken($this->access_token)
->post(route('api.account.update'), $this->accountDetailsPayload());
// Handle the response based on success or error
$response->successful()
? $this->handleSuccess($response)
: $this->handleFailure($response);
}
/**
* Handle the success response
*
* @param \Illuminate\Http\Client\Response $response
* @param string|null $endpoint
*/
protected function handleSuccess($response, $endpoint = null)
{
// If the endpoint is 'account_details', skip flashing the message
if ($endpoint !== 'account_details') {
session()->flash('message', $response->json()['message']);
}
// Additional success handling logic (if any)
if ($endpoint == 'account_details') {
// Merge the mainData and profileData arrays
$account_details = array_merge(
$response->json()['data'][array_key_first($response->json()['data'])],
$response->json()['data'][array_key_first($response->json()['data'])]['profile']
);
// Get the keys from accountDetailsPayload method
$fields = array_keys($this->accountDetailsPayload());
// Loop through the fields and assign values dynamically from the merged array
foreach ($fields as $field) {
if (isset($account_details[$field])) {
$this->$field = $account_details[$field];
}
}
}
}
/**
* Handle the error response
*
* @param \Illuminate\Http\Client\Response $response
*/
protected function handleFailure($response)
{
// Flash a general error message
session()->flash('message', $response->json()['message']);
// Get the error messages from the response
$response_errors = $response->json()['errors'][array_key_first($response->json()['errors'])];
// Store the error messages in the session
foreach ($response_errors as $field => $messages) {
if (is_array($messages)) {
foreach ($messages as $message) {
session()->flash("{$field}_error", $message);
}
}
}
}
/**
* Render the registration component view
*
* @return \Illuminate\View\View
*/
public function render()
{
return view('livewire.dash.update-account');
}
}
API Endpoint Handler - handleAccountUpdate()
<?php
namespace App\Http\Controllers;
use App\Mail\SuccessfulDeletionMail;
use App\Models\User;
use App\Utilities\AppUtilities;
use Illuminate\Http\JsonResponse;
use Illuminate\Support\Facades\Mail;
use Throwable;
class BaseUserController extends Controller
{
/**
* Handle the update account logic, including file uploads and profile updates.
*/
protected function handleAccountUpdate(array $data): JsonResponse
{
try {
// Get the current user from the request
$user = request()->user();
// Handle file uploads for the user's profile
$data = AppUtilities::handleFileUploads($data, $user->profile);
// Update the user's account and profile
$user->update($data) && $user->profile?->update($data);
// Return a success response
return AppUtilities::generateApiResponse(true, 'Your account has been successfully updated.', [
'user' => $user
]);
} catch (Throwable $e) {
// Return an error response if something fails
return AppUtilities::generateApiResponse(false, 'An error occurred during account update.', [
'exception' => $e->getMessage()
]);
}
}
}
Helper Function - handleFileUploads()
/**
* Handles file uploads, replacing existing files if necessary.
*
* @param array $data The existing data array where file paths will be stored.
* @param mixed $entity The entity to check for existing file paths.
*
* @return array The updated data array with new file paths.
*/
public static function handleFileUploads(array $data, $entity): array
{
foreach (config('storage') as $file_key => $directory) {
// Check if a file was provided in the request
if (request()->hasFile($file_key)) {
// Get the existing file path from the entity
$existing_file = $entity->$file_key ?? null;
// If a file exists, delete it and upload the new file
if ($existing_file) {
Storage::disk('public')->delete(str_replace('/storage/', '', $existing_file));
}
// Store the new file and update the data array
$data[$file_key] = Storage::url(request()->file($file_key)->store($directory, 'public'));
}
}
// Return the updated data array
return $data;
}