Search code examples
phplaravellaravel-livewire

How does model's attributes default to original in Livewire Laravel


I originally had ma DB eloquent call in the mount() method in the livewire component, but then I felt like it is not actually called only once as my attributes were rewritten, so I moved them to Laravel's controller.

Now in Laravel Controller

public function photos() {
    $photos = Image::all();

    foreach ($photos as $photo) {
        $photo->imgLqPath = Storage::disk('s3')->temporaryUrl($photo->path025, now()->addHour());
        $photo->imgPath = Storage::disk('s3')->temporaryUrl($photo->path, now()->addHour());
    }

    return view('cs.photos', [
        'photos' => $photos
    ]);
}

then pass these photos to the component with @livewire('cs.photos', ['photos' => $photos])

now in component's view, I looped them out to see what is happening to them

@foreach ($photos as $ph)
    <pre>{{ json_encode($ph->getAttributes(), JSON_PRETTY_PRINT|JSON_UNESCAPED_UNICODE) }}</pre>
@endforeach

resulting in

{
    "id": 204,
    "name": "long_name.png",
    "path": "long_path.png",
    "description": null,
    "path025": "long_path.png",
    "user_id": 5,
    "coord_id": null,
    "gpsx": 12.3,
    "gpsy": 45.56,
    "imageable_type": "App\\CS",
    "imageable_id": 193,
    "created_at": "2021-07-24 14:02:30",
    "updated_at": "2021-07-24 14:02:30",
    "imgLqPath": "https:\/\/path...",
    "imgPath": "https:\/\/path..."
}

With this I am happy, however, whenever I call any action via wire:click (i also tried .stop and .prevent.

<img
    class="card-img-top"
    src="{{ $photo->imgLqPath }}"
    alt="..."
    height="200px"
    style="object-fit: contain;"
    wire:click="selectPhoto({{ $photo }})"
>

(does not matter whether the action does something, the problem happens even with the blank body of the function)

public function selectPhoto(Image $photo)
{
    // $this->photo = $photo;
    // $this->emit('photoSelected', 'test');
}

My loop for photo attributes changes to this

{
    "id": 204,
    "name": "long_name.png",
    "path": "long_path.png",
    "description": null,
    "path025": "long_path.png",
    "user_id": 5,
    "coord_id": null,
    "gpsx": 12.3,
    "gpsy": 45.56,
    "imageable_type": "App\\CS",
    "imageable_id": 193,
    "created_at": "2021-07-24 14:02:30",
    "updated_at": "2021-07-24 14:02:30"
}

seems like their attributes were reset to the original, but I'm clueless why that could happen.

update:

To the livewire component controller, I added:

public function hydratePhotos($value) {
    dd($value);
}

Which dumps a collection of photos without the path attributes, so this seems to be the problem. I'm not sure how to solve it yet tho, so if no answer comes here before I do, I will update again.


Solution

  • Unlike what one might "feel", the Livewire requests are stateless - meaning that any data is sent back and forth between requests - and some data is retrieved from the database on each request. For models and collection of models, they are queried from the database between each request - only the models class and key is stored in the request.

    This means that when your component rehydrates on subsequent requests, you have to re-apply those custom properties. If we assume that you have a collection in the property $photos on your Livewire component, you can do

    public function hydrate()
    {
        foreach ($this->photos as $photo) {
            $photo->imgLqPath = Storage::disk('s3')->temporaryUrl($photo->path025, now()->addHour());
            $photo->imgPath = Storage::disk('s3')->temporaryUrl($photo->path, now()->addHour());
        }
    }