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

muuucho's avatar
Level 11

Livewire component performs unexpected update

In my teams app I want to halt a user to update a model if another user has updated a model before the user updates his version of the model. For that, I use the column updated_at. So I mount the original timestamp updated_at as $lastUpdatedAt. Then when the user tries to update the post, the script loads the post again and compares the posts $updated_at with $lastUpdateAt. If someone else has been updating the post if they are not the same. To prevent overwriting that user's data I want to redirect with a flash warning and urge the user to try again. Surprisingly I can halt the script with dd() but when I try to redirect the post gets updated. Pubs/Form.php

	<?php

namespace App\Livewire\Pubs;

use App\Models\Pub;
use App\Models\Tag;
use App\Models\Type;
use Auth;
#use Illuminate\Console\View\Components\Alert;
use Livewire\Component;
use Illuminate\Contracts\View\View;
use Illuminate\Validation\Rule;
use Jantinnerezo\LivewireAlert\LivewireAlert;
class Form extends Component
{
    use LivewireAlert;
    public ?Pub $pub = null;
    public string $name = '';
    public ?int $type_id;
    public bool $editing = false;
    public array $tags = [];
    public $vat = false;
    public string $gender = '';
    public array $listsForFields = [];
    public string $lastUpdatedAt = ''; // To prevent updating collisions

    public function mount(Pub $pub): void
    {
        $this->initListsForFields();
        if (! is_null($this->pub)) {
            $this->pub = $pub;
            $this->editing = true;
            $this->name = $this->pub->name;
            $this->type_id = $this->pub->type_id;
            $this->tags = $this->pub->tags()->pluck('tags.id')->toArray();
            $this->vat = $this->pub->vat;
            $this->gender = $this->pub->gender;
            $this->lastUpdatedAt = $this->pub->updated_at;
        }
    }

    public function save(): void // Methods are called Actions. They do things, like after a form submit that has wire:submit
    {
        $this->validate();
        if (is_null($this->pub)) {
            $this->pub = Pub::create(
                array_merge($this->only('name', 'type_id', 'vat', 'gender'),  [
                    'team_id' => Auth::user()->team_id,
                    'cb' => Auth::id(),
                    'ub' => Auth::id(),
                    'ua' => now()
                ])
            );
            $msg_str = 'created';
        } else {
            if($this->pub->team_id !== Auth::user()->team_id){ // Some thong fishy is going on, halt script
                abort(404);
            }
            // Redirect if another user in the team has updated the post
            $pub_old = Pub::findOrFail($this->pub->id);
            if(date('Y-m-d H:i:s', strtotime($pub_old->updated_at)) !== (date('Y-m-d H:i:s', strtotime($this->lastUpdatedAt)))){
                #dd('stop');  // WORKS!!
                $this->flash('warning', 'Post could not be updated because someone else has been updated it simultaneously, please try again');
                $this->redirect('/pubs'); // DOESN'T WORK
            }
            $this->pub->update(
                array_merge($this->only('name', 'type_id', 'vat', 'gender'), [
                    'team_id' => Auth::user()->team_id,
                    'ub' => Auth::id()
                ]) // Add team_id on update just to be sure no one has been posting a malicious team_id
            );
            $msg_str = 'updated';
        }
        $this->pub->tags()->sync($this->tags);
        $this->flash('success', 'Post successfully ' . $msg_str);
        $this->redirect('/pubs');
    }

    public function render(): View
    {
        return view('livewire.pubs.form');
    }

    protected function rules(): array
    {
        return [
            'name' => ['required', 'string'],
            'type_id' => ['required', 'integer', 'exists:types,id'],
            'tags' => ['required', 'array'],
            'vat' => ['bool'],
            'gender' => ['required', Rule::in(['male', 'female'])],
        ];
    }

    protected function initListsForFields(): void
    {
        $this->listsForFields['types'] = Type::pluck('name', 'id')->toArray();
        $this->listsForFields['tags'] = Tag::pluck('name', 'id')->toArray();
    }
}

0 likes
2 replies
LaryAI's avatar
LaryAI
AI
Best Answer
Level 58

To address the issue where the redirect is not working as expected in your Livewire component, you need to ensure that the redirect is properly handled within the Livewire lifecycle. The redirect method should be called using Livewire's built-in redirect functionality.

Here's the updated save method with the necessary changes:

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

    if (is_null($this->pub)) {
        $this->pub = Pub::create(
            array_merge($this->only('name', 'type_id', 'vat', 'gender'),  [
                'team_id' => Auth::user()->team_id,
                'cb' => Auth::id(),
                'ub' => Auth::id(),
                'ua' => now()
            ])
        );
        $msg_str = 'created';
    } else {
        if($this->pub->team_id !== Auth::user()->team_id){ // Some thong fishy is going on, halt script
            abort(404);
        }

        // Redirect if another user in the team has updated the post
        $pub_old = Pub::findOrFail($this->pub->id);
        if(date('Y-m-d H:i:s', strtotime($pub_old->updated_at)) !== (date('Y-m-d H:i:s', strtotime($this->lastUpdatedAt)))){
            $this->flash('warning', 'Post could not be updated because someone else has been updated it simultaneously, please try again');
            return redirect()->to('/pubs'); // Use Livewire's redirect functionality
        }

        $this->pub->update(
            array_merge($this->only('name', 'type_id', 'vat', 'gender'), [
                'team_id' => Auth::user()->team_id,
                'ub' => Auth::id()
            ]) // Add team_id on update just to be sure no one has been posting a malicious team_id
        );
        $msg_str = 'updated';
    }

    $this->pub->tags()->sync($this->tags);
    $this->flash('success', 'Post successfully ' . $msg_str);
    return redirect()->to('/pubs'); // Use Livewire's redirect functionality
}

Explanation:

  1. Validation: The validate method is called to ensure the data is correct.
  2. Create or Update: Depending on whether $this->pub is null, it either creates a new record or updates an existing one.
  3. Team Check: It checks if the team_id matches the authenticated user's team.
  4. Concurrency Check: It compares the updated_at timestamps to detect if another user has updated the post.
  5. Flash Message and Redirect: If another user has updated the post, it sets a flash message and uses redirect()->to('/pubs') to redirect the user. This ensures that the redirect is handled correctly within the Livewire lifecycle.

By using redirect()->to('/pubs'), you ensure that the redirect is properly handled by Livewire, which should resolve the issue you're experiencing.

1 like
muuucho's avatar
Level 11

It works, but strangely the Livewire docs says "Because Livewire works with Laravel's redirection system, you can use any notation you are used to like redirect('/foo'), redirect()->to('/foo'), redirect()->route('foo')."

Please or to participate in this conversation.