Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

marius_bosinceanu's avatar

Laravel/Jetstream/Livewire

Hello ! I need some support for the next issue.

My app contain a form. The form call to a function. In that function I'm making a validation and if the validation fails, than the app, via Livewire, it throw an error so the user sees what he have to complete.

Configuration: Laravel=10.48.18, Jetstream=3.3.3, Livewire=2.12.8

0 likes
19 replies
marius_bosinceanu's avatar

edit-profile-information-form.blade.php:

<x-slot name="description">
    {{ __('U kunt hier uw gegevens aanvullen of aanpassen en bewaren.

Wijzigingen worden door Miedema Assisstance gecontroleerd en doorgevoerd. Zodra dit is uitgevoerd ontvangt u een bevestiging per e-mail.') }}

<x-slot name="form">


    <div class="col-span-10 sm:col-span-4">


        <x-button wire:click="confirmResetData">
            {{ __('ANNULEER') }}
        </x-button>

        <x-button wire:loading.attr="disabled" wire:submit.prevent="submit">
                {{ __('WIJZIGING AANVRAGEN') }}
        </x-button>

        <!-- Add the comment in red color -->
        <div>
            <br>
        <p style="color: red; font-size: smaller;">
            <em>* - Invoervelden gemarkeerd met een sterretje zijn verplicht</em>
        </p>
        </div>

    </div>

    <div class="col-span-6 sm:col-span-4 font-semibold">
            {{ __('Naam en adresgegevens') }}
    </div>

    <!-- 1.1. Name -->
    <div class="col-span-6 sm:col-span-4 ">
        <x-label for="name" value="{{ __('Naam') }}" />
        <x-input id="name" type="text" class="mt-1 block w-full bg-gray-200" wire:model="state.name" autocomplete="name" readonly/>
        <x-input-error for="name" class="mt-2" />
    </div>

    <!-- 1.3. Straatnaam -->
    <div class="col-span-6 sm:col-span-4 ">
        <x-label for="address" value="{{ __('Straatnaam *') }}" />
        <x-input id="address" type="text" class="mt-1 block w-full" wire:model="state.address" required autocomplete="address"/>
        @error('address') <span class="error">{{ $message }}</span> @enderror
        <x-input-error for="state.address" class="mt-2" />
    </div>


    <div class="col-span-10 sm:col-span-4">
        <x-button wire:click="confirmResetData">
            {{ __('ANNULEER') }}
        </x-button>

        <x-button wire:loading.attr="disabled" wire:submit.prevent="submit">
            {{ __('WIJZIGING AANVRAGEN') }}
        </x-button>

    </div>
</x-slot>
marius_bosinceanu's avatar

UpdateProfileInformationForm.php

namespace App\Http\Livewire;

use GearboxSolutions\EloquentFileMaker\Support\Facades\FM; use Illuminate\Support\Facades\Auth; use Laravel\Fortify\Contracts\UpdatesUserProfileInformation; use Livewire\Component; use Livewire\WithFileUploads;

class UpdateProfileInformationForm extends Component { use WithFileUploads;

public $state = [ 'address' => '', ] ; protected array $rules = [ 'address' => 'required|string|min:5|max:255', ];

/**
 * The new avatar for the user.
 *
 * @var mixed
 */
public $photo;

/**
 * Determine if the verification email was sent.
 *
 * @var bool
 */
public $verificationLinkSent = false;

/**
 * Prepare the component.
 *
 * @return void
 */
public function mount()
{

    $user = Auth::user();

    $this->state = array_merge([
        'email' => $user->email,
    ], $user->withoutRelations()->toArray());
}

/**
 * Update the user's profile information.
 *
 * @param  \Laravel\Fortify\Contracts\UpdatesUserProfileInformation  $updater
 * @return void
 */
public function updateProfileInformation(UpdatesUserProfileInformation $updater)
{
    $this->resetErrorBag();

    $updater->update(
        Auth::user(),
        $this->photo
            ? array_merge($this->state, ['photo' => $this->photo])
            : $this->state
    );

// if (isset($this->photo)) { // return redirect()->route('profile.show'); // }

    $this->emit('saved');
    $this->emit('refresh-navigation-menu');
}

/**
 * Delete user's profile photo.
 *
 * @return void
 */
public function deleteProfilePhoto()
{
    Auth::user()->deleteProfilePhoto();
    $this->emit('refresh-navigation-menu');
}

/**
 * Sent the email verification.
 *
 * @return void
 */
public function sendEmailVerification()
{
    Auth::user()->sendEmailVerificationNotification();
    $this->verificationLinkSent = true;
}

/**
 * Get the current user of the application.
 *
 * @return mixed
 */
public function getUserProperty()
{
    return Auth::user();
}

public function redirectToUpdate()
{
    // Perform any necessary actions before redirecting
    $nextPageUrl = 'dashboard';
    return redirect()->to($nextPageUrl);
}

public function cancelUpdate()
{
    $param = $this->state['_fkKandidaatnr'];

    FM::layout('kandidaat_wijzig')->script('Cancel_Wijziging_aanvragen')->scriptParam($param)->performScript();
    FM::layout('kandidaat_wijzig')->script('Cancel_Wijziging_aanvragen')->scriptParam($param)->performScript();
    $nextPageUrl = 'dashboard';
    return redirect()->to($nextPageUrl);
}

public function redirectToEdit()
{
    // Perform any necessary actions before redirecting
    $nextPageUrl = 'user/profile-edit';
    return redirect()->to($nextPageUrl);
}

public function resetData()
{
    $this->reset('state');
    return redirect()->to('user/profile-show');
}



public function confirmResetData()
{
    $nextPageUrl = 'user/profile-cancel';
    return redirect()->to($nextPageUrl);

}

/**
 * Render the component.
 *
 * @return \Illuminate\View\View
 */
public function render()
{
    //return a view base on url request
    $segment = request()->segment(2);

    if ($segment == 'profile-show') {
        return view('profile.update-profile-information-form');
    }elseif ($segment == 'profile-edit') {
        return view('profile.edit-profile-information-form');
    }elseif ($segment == 'profile-confirm') {
        return view('profile.confirm-profile-information-form');
    }elseif ($segment == 'profile-cancel') {
        return view('profile.cancel-information-form');
    }
    return view('profile.update-profile-information-form');
}

public function redirectToConfirm()
{

    try {
    $this->validate();

        $updater = app(UpdatesUserProfileInformation::class);
        $this->updateProfileInformation($updater);

    return redirect()->to('user/profile-confirm');
} catch (\Exception $e) {
    // If validation fails, set the error flag
    $this->addError('address', $e->getMessage());

    // Optionally, you can rethrow the exception if you want to handle it elsewhere
    return redirect()->to('user/profile-edit');


}

} }

marius_bosinceanu's avatar

The issue is redirectToConfirm() don't throw the errors.

Where I'm doing wrong ?

aleahy's avatar

The error is in how you define the address property and its rule:

public $state = [ 'address' => '', ] ; 
protected array $rules = [ 'address' => 'required|string|min:5|max:255', ];

With this rule, livewire is looking for a variable called address, but it is state.address.

It should be

protected array $rules = [ 'state.address' => 'required|string|min:5|max:255', ];
marius_bosinceanu's avatar

BTW, the error message I've receive dd($e->getMessage()); , it's the correct one .

I can't show that error message.

aleahy's avatar

@marius_bosinceanu Oh - I didn't notice you were catching the exception. Why are you doing that? Livewire would just return to the component with the error message without you needing to do anything.

Your sessions solution is just bypassing livewire entirely.

marius_bosinceanu's avatar

@aleahy , if I'm not catching the exceptions, the app is going to redirect me to a page which I don't want.

marius_bosinceanu's avatar

@aleahy The back page contain livewire component, the same fields but readonly. I want to remain on the same page. Much more, If I remember well, there is a restrict redirect, but I forgot the syntax.

marius_bosinceanu's avatar

BTW, now I meet the situations where the old data (data which doesn't contain any validation rules ), is disappearing after press save (taking in balance the others fields didn't validate).

aleahy's avatar

@marius_bosinceanu The way livewire works - it sends ajax requests to update the component until there is a genuine redirect occurring somewhere else (such as a successful confirm). So the front end is not being refreshed in anyway like the back button is being hit. But the php side IS being completely renewed every time and needs to be hydrated from the state sent from the front end. It sounds like something is going missing in this step, so it is unable to send it back.

Try and make sure that every aspect of your page that needs state in the front and backend are completely defined in the component. It worries me that you are using the $state variable array with only address defined on the component, yet I see state.name in your form. Is this what is going missing?

1 like
marius_bosinceanu's avatar

@aleahy , in the same file, there is a function :

public function updateProfileInformation(UpdatesUserProfileInformation $updater) { $this->resetErrorBag();

    $updater->update(
        Auth::user(),
        $this->photo
            ? array_merge($this->state, ['photo' => $this->photo])
            : $this->state
    );


    $this->emit('saved');
    $this->emit('refresh-navigation-menu');
}

This function is calling another function, from Fortify:

public function update(User $user, array $input): void { Validator::make($input, [ 'name' => ['required', 'string', 'max:255'], 'email' => ['required', 'email', 'max:255', Rule::unique('users')->ignore($user->id)], 'photo' => ['nullable', 'mimes:jpg,jpeg,png', 'max:1024'],

    ])->validateWithBag('updateProfileInformation');

This one is redirecting on the mentioned page.

So, the answer, is yes, U are right.

I imagine I have to transfer all the validation there, right ?

marius_bosinceanu's avatar

Now I can see there is an error.

I have in url - user/profile-edit, the correct one, but it renders the wrong one.

aleahy's avatar

I just noticed what you do at the end of the try - catch.

What if you just throw the exception instead? You've added the error to Livewire's message bag.

1 like
marius_bosinceanu's avatar

@aleahy , same behave. It renders the errors for profile-edit (I can see it on debugbar), which is great, but viewed renders the previous page, the one which have all the fields read only.

marius_bosinceanu's avatar

My bad, my bad !

public function render() { //return a view base on url request $segment = request()->segment(2);

    if ($segment == 'profile-show') {
        return view('profile.update-profile-information-form');
    }elseif ($segment == 'profile-edit') {
        return view('profile.edit-profile-information-form');
    }elseif ($segment == 'profile-confirm') {
        return view('profile.confirm-profile-information-form');
    }elseif ($segment == 'profile-cancel') {
        return view('profile.cancel-information-form');
    }
    return view('profile.edit-profile-information-form');
}

This was causing problems.

Initially, the last return, was on the update profile.edit-profile-information-form (the one with read only fields). Changed with edit one and now is working.

Thank U very much, @aleahy !

marius_bosinceanu's avatar

Yheap, this issue deals with the Session:

public function redirectToConfirm()
{

    try {
    $this->validate();

        $updater = app(UpdatesUserProfileInformation::class);
        $this->updateProfileInformation($updater);

    return redirect()->to('user/profile-confirm');
} catch (\Exception $e) {

        session()->flash('error_message', $e->getMessage());
        session()->flash('error_field', 'state.address');


        // Optionally, you can rethrow the exception if you want to handle it elsewhere
    return redirect()->to('user/profile-edit')->with('error', $e->getMessage());


}
}

And the blade file have to be changed to :

    <div class="col-span-6 sm:col-span-4 ">
        <x-label for="address" value="{{ __('Straatnaam *') }}" />
        <x-input id="address" type="text" class="mt-1 block w-full" wire:model="state.address" required autocomplete="address"/>
        @if (session('error_message') && session('error_field') === 'state.address')
            <span class="error" style="color: red; font-style: italic;">{{ session('error_message') }}</span>
        @endif
    </div>

Please or to participate in this conversation.